Delphi SQLite3 using ZeosLib, how to create a database and a table? - database

I want create a database and a table programmatically with my Delphi Program on Windows 7 using Zeoslib component. From what I have found online so far, Zeoslib is expecting database to be created before using it. If so, is there a way to create a database and a table using Zeoslib tools.

Normal this question will be closed because you did not show what have you tried so far.
With ZeosLib it's easy
Safety Note:
Of course you should use parameterized queries. only in order to simplify the procedure, it has been omitted here
Create the Database
procedure TForm1.CreateClick(Sender: TObject);
begin
ZConnection1.Protocol:='sqlite-3';
ZConnection1.Database:='F:\Programme\stack\SQLite\Database.sqlite';
ZConnection1.Connect;
ZConnection1.Disconnect;
end;
Create a Table and Insert
procedure TForm1.CreateInsertClick(Sender: TObject);
begin
ZQuery1.SQL.Text := 'CREATE TABLE hardware (id INTEGER PRIMARY KEY, compname VARCHAR(30), username VARCHAR(30), model VARCHAR(30))';
ZQuery1.ExecSQL;
ZQuery1.SQL.Text := 'CREATE INDEX sHardware ON hardware(compname)';
ZQuery1.ExecSQL;
ZQuery1.SQL.Text := 'INSERT INTO hardware(id, compname, username, model) VALUES (1, "AMD8537", "OMonge", "Gigabyte");';
ZQuery1.ExecSQL;
end;
To see Values Connect again
procedure TForm1.ConnectClick(Sender: TObject);
begin
ZConnection1.Connect;
end;
Show Values
procedure TForm1.OpenClick(Sender: TObject);
begin
ZQuery1.SQL.Text := 'SELECT id, compname FROM hardware';
ZQuery1.Open;
end;
Form
Running

If database file does not exists - SQLite creates it on connect.
Below is a very simple but functioning example:
procedure TForm1.Button1Click(Sender: TObject);
begin
ZConnection1.Protocol := 'sqlite-3';
ZConnection1.Database := 'foo.s3db';
if not FileExists('foo.s3db') then
begin
ZConnection1.Connect;
ZConnection1.ExecuteDirect('create table foo (bar integer)');
end
else
ZConnection1.Connect;
ZConnection1.Disconnect;
end;

Related

write multipe statments in snowflake

Hi i have one doubt in snowflake how to write multiple update stments using stored procedure.
i have tried like below
create or replace procedure sp_multipleupdate()
returns table()
lANGUAGE sql
as
$$
declare res rsultset(
update TEST.PUBLIC.DEPT set Dname='PM' where deptid=10;
update TEST.PUBLIC.emp set name='veavi' where deptno=20;
update TEST.PUBLIC.loc set locname='del' where id=5;
)
begin
return table(res);
end;
$$;
getting error :
000006 (0A000): Multiple SQL statements in a single API call are not supported; use one API call per statement instead.
Syntax error: unexpected '('. (line 2)
please let me know how to write query to achive this task in snowflake server .
Multiple SQL statements inside the resultset are not supported.
Rather than writing the UPDATE statements like that I would create a more generic procedure and pass arguments to it, so maybe split the above one in 3 procedures since these UPDATE statements are for different tables.
Here is a sample of a generic stored procedure:
create or replace procedure find_invoice_by_id_via_execute_immediate(id varchar)
returns table (id integer, price number(12,2))
language sql
as
declare
select_statement varchar;
res resultset;
begin
select_statement := 'SELECT * FROM invoices WHERE id = ' || id;
res := (execute immediate :select_statement);
return table(res);
end;
You can read more here.

Delphi how to save #temptable data into another table with dataset/dataSetProvider/ADOQuery?

My code is like:
with ArrangementClientDataSet do
begin
close;
dataset.commandText := 'exec getArrangementData';
open;
edit;
end;
In procedure the code is like
select cyear,ccode from #arrangement
So when I click query button the dbgrid shows the data.
And now I want to add a save button so that when I click, the data can be saved into another table 'arrangement'.
The code on Save button is:
procedure SaveToolButtonClick(Sender:TObject);
begin
with ArrangementClientDataSet do
begin
first;
ApplyUpdates(0);
end;
end;
I tried to set
procedure DataSetProviderGetTableName(Sender:TObject;DataSet:TDataSet;var TableName :String);
begin
TableName := 'arrangement';
end;
procedure ADOQueryAfterOpen(DataSet:TDataSet);
begin
with ArrangementADOQuery do
begin
fieldByName('cyear').providerFlags := [pfInUpdate,pfInKey];
end;
end;
But it doesn't work, SQL Server Profiler can't catch the insert SQL.
If I change a value in dbgrid and then click the save button SQL Server Profiler can catch a update SQL like
update arrangement set ccode='1' where pfInKeyField = '2022'
But it's not right because data is not yet contained in table
So what should I do?

CREATE/ALTER procedure using Delphi

I've got a small problem that I could not find an answer to. I need to create/alter some procedures from Delphi.
So this is my code that take the code from a file and tries to execute it.
procedure TfrmMainApp.actRulezaScriptExecute(Sender: TObject);
var
j: Int32;
sql: string;
commandFile: TextFile;
Linie: string;
affRows: Int32;
err: string;
begin
for j := 0 to filesToExecute.Count - 1 do
begin
sql := 'USE ' + DBName + #10#13;
sql := sql + ' GO ' + #10#13;
AssignFile(commandFile, filesToExecute[j]);
Reset(commandFile);
while not EOF(commandFile) do
begin
Readln(commandFile, Linie);
sql := sql + #10#13 + Linie;
end;
dmMainScriptRun.ExecuteCommand(sql, err, affRows);
if err <> '' then
break;
Memo1.Lines.Add('Affected rows:' + IntToStr(affRows));
end;
end;
function TdmMainScriptRun.ExecuteCommand(sqlCommand: string; var err: string;
var affRows: Int32): Boolean;
begin
err := '';
try
with cmd do
begin
CommandText := sqlCommand;
Execute(affRows, EmptyParam);
end;
except
on E: Exception do
begin
err := E.Message;
end;
end;
end;
So my file looks like this
CREATE PROCEDURE sp_TestSebi
AS
print ('testSebi')
My command looks like this ( it was taken from the SQL Server Profiler )
USE Test
GO
CREATE PROCEDURE sp_TestSebi
AS
print ('testSebi')
Executin this command returns err Incorrect syntax near 'GO' running the script without the GO statement return err CREATE/ALTER PROCEDURE' must be the first statement in a query batch because of the USE clause.
Is there a way I can create a procedure from Delphi? I need the use statement because i'm trying to execute a script on multiple databases.
The way you are attempting this can be improved in several ways.
Firstly, try this:
Create a new VCL project
Drop a TAdoConnection and a TAdoQuery on the form. Also drop a TButton on it.
Connect the TAdoQuery to the TAdoConnection.
Set the TAdoConnection to connect to your Sql Server
in the code below, modify the scUse constant to refer to your target database
and scCreateView to refer to a non-existing view and a valid table in your db. This is to ensure that the Create View will not fail because the view already exists or the table does not exist.
Run the code and you should get a complaint that the Create View refers to an invalid object name, because the AdoConnection isn't connected to your target database when the Create View executes.
Then change KeepConnection to True and retest. This time the view should be successfully created.
const
scUse ='use m4common';
scCreateView = 'create view vwtest as select * from btnames';
procedure TForm1.Button1Click(Sender: TObject);
begin
AdoConnection1.DefaultDatabase := 'Master';
// The point of changing the default database in the line above
// is to ensure for demo purposes that the 'Use' statement
// is necessary to change to the correct db.
AdoConnection1.KeepConnection := False; // retest with this set to True
AdoQuery1.SQL.Text := scUse;
AdoQuery1.ExecSQL;
AdoQuery1.SQL.Text := scCreateView;
AdoQuery1.ExecSQL;
end;
So, the point of KeepConnection is to allow you to execute two or more Sql batches in the same connection context and to satisfy the server that the Create View statement (or similar) can be the first statement in the batch, and at the same time, the database to which the Create View applies is the same one as you "USEd" in the previous batch.
Secondly, your AssignFile ... while not Eof is unnecessarily long-winded and error-prone. Try something like this instead:
var
TL : TStringList;
begin
TL := TStringList.Create;
try
for j := 0 to filesToExecute.Count - 1 do
begin
sql := 'USE ' + DBName + #13#10;
sql := sql + ' GO ' + #13#10;
TL.BeginUpdate; // for better performance with longer scripts
TL.LoadFromFile(filesToExecute[j]);
TL.Insert(0, sql);
TL.EndUpdate;
// execute the Sql here etc
end;
finally
TL.Free;
end;
end;
Note that I've reversed the order of your #10 and #13 so that it is correct.
The other point is that as the Lines property of your Memo already has a LoadFromFile method, you don't really need my temporary TStringList, TL, because you could do the load into your memo (though you might prefer to keep the two uses separate).
You can drop the connection each time and specify the USE in the params. In fact if you are clever you can establish one connection per db and parallelize the whole thing.

Getting a count of the number of statements in a TransactSql batch

(For readers who don't use Delphi: Although the following is couched in terms of Delphi coding, my actual technical question isn't Delphi-specific, but is about how to find out how the Sql Server will "understand" a TransactSql batch submitted to it. A "TAdoQuery" is a Delphi class which basically wraps an ADO Command and a RecordSet and submits a TSql batch to a Sql Server. Usually, using the TAdoQuery, the batch is a single statement, but my q is specifically concerned with the possibility that the batch may contain more than one statement.)
Suppose I have a TAdoQuery whose Sql.Text contains a TransactSql batch comprising
one or more statements S1[...Sn].
What I'm trying to do is to find out without executing the batch whether a) the first (or only) statement, S1, in the batch will return a result set (even if empty) e.g by dint of it being a SELECT statement or an invocation of a stored procedure or table function, or whatever, AND b) how many statements the server thinks there are in the batch.
Regular users of Delphi's TAdoQuery will know that it's easy but slightly messy to test whether the first (or only) statement in a batch returns a result set just by calling TAdoQuery.Open. If it does, then it retrieves that result set, but if doesn't, then calling .Open will provoke an exception.
So, instead, I do something like this:
type
TMyDataSet = class(TDataSet);
procedure TForm1.Button1Click(Sender: TObject);
begin
if AdoQuery1.Active then
AdoQuery1.Close;
AdoQuery1.FieldDefs.Clear;
TMyDataSet(AdoQuery1).OpenCursor(True);
AdoQuery1.FieldDefList.Update;
//AdoQuery1.FieldList.Update;
//Listbox1.Items.Assign(AdoQuery1.FieldList);
end;
The call to .OpenCursor with its InfoQuery param set to true causes the AdoQuery's FieldDefs
to be populated iff the first statement in its Sql would return a result set, but, unlike calling .Open, it will not cause the batch to be executed.
So far, so good. Here's my question:
How (via the AdoQuery or otherwise) do I get the Sql Server to tell me how many
statements it thinks the batch contains? (I think I may have stumbled on a way (subject to a lot more testing), but am interested in whether anyone knows an "official" technique for doing this.)
Btw, for now I'm using an antique (Sql Server 2000!) server via its OleDB driver for Sql Server.
You can use the SET SHOWPLAN_ALL function to analyze the statement in SQL server instead of executing the query. Please note that you can't use this functionality with TADOQuery but only with a TADOCommand object (Like TADOConnection.Execute).
test table:
USE [TestCustomer]
GO
CREATE TABLE [dbo].[Tbl_test](
[Id] [int] NULL,
[col1] [varchar](50) NULL
) ON [PRIMARY]
GO
small demo program:
program SO27007086;
{$APPTYPE CONSOLE}
uses
ActiveX,
Db,
AdoDb,
SysUtils;
var
DbConn : TADOConnection;
function GetNumberOfStatements(SQLQuery: String): Integer;
var
Rs : _RecordSet;
LastId : Integer;
begin
Result := 0;
LastId := -1;
DbConn.Execute('SET SHOWPLAN_ALL ON');
Rs := DbConn.Execute(SQLQuery, cmdText, []);
while not Rs.EOF do
begin
if Rs.Fields['StmtId'].Value <> LastId then
begin
Inc(Result);
LastId := Rs.Fields['StmtId'].Value;
end;
if Rs.Fields['Parent'].Value = 0 then
Writeln(Rs.Fields['Type'].Value);
Rs.MoveNext;
end;
DbConn.Execute('SET SHOWPLAN_ALL OFF');
end;
begin
try
try
CoInitialize(nil);
DbConn := TADOConnection.Create(nil);
try
DbConn.ConnectionString := 'Provider=SQLOLEDB;Integrated Security=SSPI;Initial Catalog=TestCustomer;Data Source=localhost\SQLEXPRESS;MARS Connection=True;';
DbConn.Connected := True;
Writeln(GetNumberOfStatements('SELECT * FROM Tbl_test'));
Writeln(GetNumberOfStatements('SELECT * FROM Tbl_test DELETE FROM Tbl_test WHERE 1 = 2'));
Writeln(GetNumberOfStatements('SELECT * FROM Tbl_test INSERT INTO Tbl_Test (Id, Col1) VALUES (3, ''c''),(4, ''d'')'#13#10'DELETE FROM Tbl_test WHERE 1 = 2'));
finally
DbConn.Free;
end;
finally
CoUninitialize;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
Output:
SELECT
1
SELECT
DELETE
2
SELECT
INSERT
DELETE
3

Oracle dynamic database link from variables

i have 3 oracle databases: db1, db2, db3.
I have created database links from db1 to db2 and db3, called db002link and db003link.
Now i have a procedure which takes as input a date and takes different actions on tables according to that input. One of them though requires to connect to one of the db2 or db3 databases. Before the execution of the procedure, i don't not know to which one, as it depends on the data gathered by the procedure itself in previous steps.
So i need to concatenate some variables to create the db link and then connect through it.
i have the variable v_dbnumber which is varchar(3) and looks like '003' for instance and is the result of a select from a table. I have tried the following:
v_dbconn := 'db'||v_dbnumber||'link'
But then the next step, select * from s1.t1#v_dbconn gets a compilation error for the procedure: ORA-04052, ORA-00604, ORA-02019 referring to the non existing connection. But the object is shown as:
#v_dbconn instead of #db003link.
Can somebody please help me with this?
If you need the statement to be dynamic, you'd need to use dynamic SQL.
If you just want to open a cursor using a dynamically generated SQL statement, you can do something like
DECLARE
l_sql_stmt varchar2(1000);
l_dblink varchar2(100) := 'db002link';
l_rc sys_refcursor;
BEGIN
l_sql := 'select * from s1.t1#' || l_dblink;
open l_rc for l_sql;
END;
Normally, though, you're doing something with the data that you're selecting. That would generally involve using either dbms_sql or EXECUTE IMMEDIATE to execute the statement and fetch data into some local variable or collection. Assuming that the table definitions are the same in each of the databases, you could do something like
EXECUTE IMMEDIATE l_sql
BULK COLLECT INTO <<some appropriate collection>>
My solution is very similar to Justin's, though I am using a procedure with dynamic sql.
APPS#tst> CREATE OR REPLACE PROCEDURE test_dblink(
2 db_link VARCHAR2 )
3 AS
4 v_sql VARCHAR2(500);
5 v_test dual.dummy%TYPE;
6 BEGIN
7 v_sql := 'select dummy from dual#'|| db_link;
8 EXECUTE IMMEDIATE v_sql INTO v_test;
9 DBMS_OUTPUT.PUT_LINE(v_test);
10 END;
11 /
Procedure created.
APPS#tst> commit;
Commit complete.
APPS#tst>
APPS#tst>
APPS#tst>
APPS#tst> begin
2 test_dblink('db003link');
3 end;
4 /
X
PL/SQL procedure successfully completed.
This does not have error handling and it assumes one record will be returned (typically not a good assumption).

Resources