SQL database data into an array - sqlite

I currently have a program which connects to a database and displays the data on a DBGrid, however I need to extract this data so I can use it in another algorithm.
When I use the command: select scores from quiz
It shows the values on the screen. (23,55,64)
How do I get these values into an array so that
[0]=23
[1]=55
[2]=64
Thanks in advance.

Best to use TList (which is an array wrapper object). Below is an excerpt from some code that I use. TSomeRect is a record to store the field data of each row in.
function CreateQuery(pConnection: Tsqlconnection; pTransaction: TSQLTransaction): TSQLQuery;
begin
result := TSQLQuery.Create(nil);
result.Database := pConnection;
result.Transaction := pTransaction
end;
var
connect: TSQLite3Connection;
SQLQuery1: TSQLQuery;
transact: TSQLTransaction;
Query : TSQLQuery;
lst :TList<TSomeRect>;
rec :TSomeRect;
begin
lst :=TList<TSomeRect>.create;
connect:=TSQLite3Connection.create(nil);
connect.LoginPrompt := False;
connect.DatabaseName := 'c:\path\to\database.sqlite';
connect.KeepConnection := False;
transact:=TSQLTransaction.create(nil);
transact.action:=caNone;
transact.database:=connect;
connect.Transaction:=transact;
Query := CreateQuery(Connect, Transact);
Query.SQL.Text := 'select * from table';
Connect.Open;
Query.Open;
while not Query.Eof do
begin
rec.field1:= Query.FieldByName('field1').AsInteger;
rec.field2:= Query.FieldByName('field2').Asstring;
lst.add(rec);
Query.Next;
end;
Query.Close;
Connect.Close;
Query.Free;
Transact.Free;
Connect.Free;

Related

How to get the ID of the last record inserted in SQLite with Delphi 10?

Delphi 10 with Firemonkey and SQLite: After running the code below I want to get the ID of the last record inserted into an SQLite table. How do I get the last ID?
NOTE: The ID field of Table 1 is autoincrement.
var myQr: TFDQuery;
begin
myQr := TFDQuery.Create(Self);
with myQr do begin
SQL.Add('Insert into table1 values (:_id, :_name, :_dthr)');
Params.ParamByName('_id').ParamType := TParamType.ptInput;
Params.ParamByName('_id').DataType := TFieldType.ftInteger;
Params.ParamByName('_id').Value := null;
ParamByName('_name').AsString := 'name test';
ParamByName('_dthr').AsDateTime := Now;
ExecSQL;
end;
// How to get last ID? <<<<<<<<<<<<<=================
myQr.DisposeOf;
You could query last_insert_rowid if your ID column is declared as INTEGER PRIMARY KEY. In such case the column becomes alias for the ROWID. If that is your case, you can query it natively e.g. this way:
uses
FireDAC.Phys.SQLiteWrapper;
function GetLastInsertRowID(Connection: TFDConnection): Int64;
begin
Result := Int64((TObject(Connection.CliObj) as TSQLiteDatabase).LastInsertRowid);
end;
Or in common way by calling GetLastAutoGenValue method:
function GetLastInsertRowID(Connection: TFDConnection): Int64;
begin
Result := Int64(Connection.GetLastAutoGenValue(''));
end;

How can I create a new SQLite file and table at runtime using FieldDefs?

I'm using Delphi Seattle to create a brand new table in a brand new SQLite file and using only FieldDefs and non-visual code. I can create a table using the ExecSQL ('CREATE TABLE....' ) syntax but not as shown below (I get 'No such table 'MyTable' which is raised when I execute the CreateDataSet call). I'd like some solution that allows me to work with FieldDefs. This code is modelled on the example here. I notice though, that there is note regarding CreateDataSet that it only applies to TFDMemTable. Is there a runtime way of creating an SQLite table without using ExecSQL?
procedure Test;
const
MyDBFile = 'c:\scratch\hope.db';
var
Connection : TFDConnection;
DriverLink : TFDPhysSQLiteDriverLink;
Table : TFDTable;
begin
DeleteFile( MyDBFile );
DriverLink := TFDPhysSQLiteDriverLink.Create( nil );
Connection := TFDConnection.Create( nil );
try
Connection.Params.Values['DriverID'] := 'SQLite';
Connection.Params.Values['Database'] := MyDBFile;
Connection.Connected := True;
Table := TFDTable.Create( nil );
try
Table.TableName := 'MyTable';
Table.Connection := Connection;
Table.FieldDefs.Add( 'one', ftString, 20 );
Table.FieldDefs.Add( 'two', ftString, 20 );
Table.CreateDataSet;
// I would add records here....
finally
Table.Free;
end;
finally
Connection.Free;
DriverLink.Free;
end;
end;
CreateDataSet is usually a local operation for initializing a client-side dataset into an empty state. If TClientDataSet is anything to go by, afaik it cannot be used create a server-side table.
To create an actual server table, I would expect to have to construct the DDL SQL to create the table and then execute it using ExecSQL on the (client-side) dataset, as you have already tried.
update
The following seems to satisfy your requirement to do everything in code, though using a TFDTable component, which doesn't surface FieldDefs, so I've used code-created TFields instead. Tested in D10 Seattle.
procedure TForm3.CreateDatabaseAndTable;
const
DBName = 'd:\delphi\code\sqlite\atest.sqlite';
var
AField : TField;
begin
if FileExists(DBName) then
DeleteFile(DBName);
AField := TLargeIntField.Create(Self);
AField.Name := 'IDField';
AField.FieldName := 'ID';
AField.DataSet := FDTable1;
AField := TWideStringField.Create(Self);
AField.Size := 80;
AField.Name := 'NameField';
AField.FieldName := 'Name';
AField.DataSet := FDTable1;
FDConnection1.Params.Values['database'] := DBName;
FDConnection1.Connected:= True;
FDTable1.TableName := 'MyTable';
FDTable1.CreateTable(False, [tpTable]);
FDTable1.Open();
FDTable1.InsertRecord([1, 'First']);
FDConnection1.Commit;
FDConnection1.Connected:= False;
end;
I expect that someone a bit more familiar than I am could do similar using a TFDMemTable's FieldDefs if it were correctly connected to a server-side component (FDCommand?) via an FDTableAdaptor.
Fwiw, I've used a LargeInt ID field and WideString Name field because trying to use Sqlite with D7 a while back, I had no end of trouble trying to use Integer and string fields.
Btw, you if you know the structure you require in advance of deployment, you might find that you get more predictable/robust results if you simply copy an empty database + table into place, rather than try and create the table in situ. Ymmv, of course.
I would NEVER dream of creating database tables using fielddefs because you wind up having tables without a proper primary key, indexes and referential integrity. The resulting tables are totally "dumbed down".
Whenever you have a "where" clause in a query the database would do a full table scan to find the records matching the query. So your database slows down (and CPU use increases) with size. That's just bad design.
Regards,
Arthur
You can use the app SQLite Expert Professional, create SQLite database.
And using FDConnection connect to the database. And use it.
Method to database SQLite, the same way that MartynA have said.
Begin
FDConnection1.Connected:=false;
FDConnection1.Params.Clear;
FDConnection1.Params.Database:='D:\SQLiteDatabase.db';
FDConnection1.ConnectionDefName:='SQLite_Demo';
FDConnection1.DriverName:='SQLite';
FDConnection1.Connected:=true;
FDTable1.Open;
End;

Return ref_cursor from for loop of cursor object

I am trying to get a ref_cursor to be assigned to a variable inside a for loop then returned at the end of a function. The loop in going through a local cursor if it gets more than 1 result.
I have noted where the error occurs in the code. I am not sure how to create a loop where i can get a ref_cursor for the current point in the loop, assign it to a variable and then return it to the function. Could someone someone help me figure out how to do that? Below is my i-th attempt at logic based of reading around the google searches.
The error is "PLS-00382: expression is of wrong type" and i know that i obviously am not assigning the correct variably type based on this error but the code below with the error is an illustration of what I want to do and what I need help accomplishing.
FUNCTION GET_PARCEL(p_lat in number, p_long in number) return sys_refcursor
IS
v_distance number(10) := 100000000;
v_shortest_dist number(10) := v_distance;
v_centroid SDO_GEOMETRY;
v_rc_ref_cursor sys_refcursor;
v_ref_geom SDO_GEOMETRY := mdsys.SDO_GEOMETRY(2001, 8311, NULL, SDO_ELEM_INFO_ARRAY(1, 1, 1), SDO_ORDINATE_ARRAY(120.3214, -10.7088));
cursor query_cursor is select * from PARCEL_TABLE where code = 20134;
BEGIN
for query_row in query_cursor loop
v_centroid := SDO_GEOM.SDO_CENTROID(query_row.geometry, 0.05);
IF (v_centroid is not null) then
v_distance := SDO_GEOM.SDO_DISTANCE(v_centroid, v_ref_geom, 0.05);
IF v_distance < v_shortest_dist THEN
v_shortest_dist := v_distance;
v_rc_ref_cursor := query_row; -- Error on this line
END IF;
ELSE
DBMS_OUTPUT.PUT_LINE('Centroid is not initialised for some reason.');
END IF;
end loop;
return v_rc_ref_cursor;
END;
As far as I know, you cannot build up a cursor. Cursors are created and maintained by the database with connections to their source data, transaction and session context, and the like.
I would suggest you declare a type, instantiate it, build up the values in the type.
When you are done, create a cursor by selecting * from table (cast (variable as your_type)).
Munge the cursor into a ref_cursor and return that.
Pro tip is to remember you have data in your table and you can use it for logic. Turns out if I use the IDENTIFIER or ROWID of the row/s which i want then i can do a where clause into a ref cursor that looks up by IDENTIFIER or ROWID.
eg.
open v_rc_ref_cursor for select * from PARCEL_TABLE n where n.identifier = query_row.identifier;
super simple stuff :)

SQLite insert on android works only once

I'm with a problem on Delphi, i create a simple app to test the mobile power of RadStudio, I created a simple app that put some data into some inputs and then add it to database when button is clicked. I followed this Embarcadero tutorial as starting point
The problem is that I only get one entry added, then no more entries are added or the list is not refreshed. Below some code:
Table creation:
procedure TTabbedForm.logAfterConnect(Sender: TObject);
begin
log.ExecuteDirect('CREATE TABLE IF NOT EXISTS lista (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,idCons INTEGER,nome TEXT,kms INTEGER,kmsAlarme INTEGER,quantidade INTEGER,quantidadeAlarme INTEGER,data INTEGER,dataAlarme INTEGER,alarmeMsg TEXT)');
end;
Add button code:
procedure TTabbedForm.btnGravarClick(Sender: TObject);
begin
try
SQLQueryInsert.ParamByName('idCons').AsInteger := PopupBoxTipo.ItemIndex;
SQLQueryInsert.ParamByName('nome').AsString := PopupBoxTipo.Text;
SQLQueryInsert.ParamByName('kms').AsInteger := StrToInt(kmsEdit.Text);
SQLQueryInsert.ParamByName('quantidade').AsInteger := StrToInt(qtdEdit.Text);
SQLQueryInsert.ParamByName('data').AsInteger := DateTimeToUnix(dtaEvento.Date);
SQLQueryInsert.ExecSQL();
lista.Refresh;
LinkFillControlToField1.BindList.FillList;
except
on e: Exception do
begin
ShowMessage(e.Message);
end;
end;
end;
If you need some more code snippet, please ask!
Thanks in advance for any reply!
Try this method it works for me
procedure TData.InsertItem(someObject: TSomeObjectClass);
var
qry: TFDQuery;
begin
qry := CreateQry( 'insert into SomeObject(id, description, something)'+
' values (:id, :description, :something);', false);
qry.Params.ParamByName('id').AsInteger := someObject.id;
qry.Params.ParamByName('description').asstring := someObject.description;
qry.Params.ParamByName('something').asstring := someObject.something;
qry.Prepare;
qry.execsql;
qry.Free;
end;
I've put and object in the parameter but you can also put the data you want to insert seperatly

PLSQL: edit cursor at runtime

I'm a bit confused
is it possible in a function/proc something like this?
CURSOR CR IS
SELECT
FROM
WHERE Y = X
and later in the body, change the X value into Z or something else, and then re-open the cursor so it fetches the rows with the Y = Z?
There are a couple of ways to do this without resorting to dynamic SQL. One option would be to use a parameterized cursor:
DECLARE
nSome_value NUMBER := 666;
CURSOR CR(parmSome_value NUMBER) IS
SELECT *
FROM SOME_TABLE
WHERE SOME_COLUMN = parmSome_value;
BEGIN
nSome_value := 123; -- change value of nSome_value
OPEN CR(nSome_value); -- pass nSome_value in as the value of the cursor parameter
-- Fetch from the cursor, do whatever
CLOSE CR;
END;
It's still static SQL but by passing a parameter to the cursor you increase the reusability of the cursor.
Another option is to use a cursor FOR loop, referencing the variable in the loop's SQL:
DECLARE
nSome_value NUMBER := 666;
BEGIN
nSome_value := 123; -- change value of nSome_value
FOR aRow IN (SELECT *
FROM SOME_TABLE
WHERE SOME_COLUMN = nSome_value)
BEGIN
-- Do something useful with the rows returned by the cursor
END LOOP;
END;
Note that in these cases you're not changing the SQL - you're just changing the values of the variable or parameter used in the query. One advantage of these approaches is that unlike dynamically generated SQL they're not vulnerable to SQL injection attacks.
Share and enjoy.
Yes, try something like this (typed quickly so syntax might not be exact)
sql VARCHAR2(255);
cur REF CURSOR;
val varchar2(100);
val := X;
sql := 'SELECT .. FROM .. WHERE Y = :val';
open cur for
sql
USING val;
close cur;
....
val := Z;
open cur for
sql
USING val;
This can also be done by using a subprograms like function instead of using a cursor as it
wil shorten the code :
create or replace function getashish(dept varchar2) return emp3 as
emp5 emp3 := emp3();
str varchar2(300);
begin
str := 'select emp1(e.last_name,l.city,e.salary) from employees e join departments d
on e.department_id = d.department_id join locations l on d.location_id=l.location_id where
d.department_name = :dept';
execute immediate str
bulk collect into emp5
using dept;
return emp5;
end;
here you have to create a object including the return types yoy want in the return and then create and table of that object:
create or replace type emp1 as object (lname varchar2(10),city varchar2(10),sal number(10));
and
CREATE OR REPLACE
type emp3 as table of emp1;

Resources