Forum > Database

Editing/reading an existing database in Lazarus.

(1/1)

Evgenius:
In my simple program I have an array of records which represent products. The records include prices, quantities, etc. All this information is being input with read(). However, I want to automatize the input of products’ price using a database. Namely, I have a sqlite3 database with names and corresponding prices of products. So when I input product name with read(), I want to connect to this database, get the corresponding products’s price and input it into my record. In the web I found many ways of creating new databases, but how do I open an EXISTING database and extract information using queries? Please, help me!

dsiders:

--- Quote from: Evgenius on November 11, 2023, 11:10:30 pm ---In my simple program I have an array of records which represent products. The records include prices, quantities, etc. All this information is being input with read(). However, I want to automatize the input of products’ price using a database. Namely, I have a sqlite3 database with names and corresponding prices of products. So when I input product name with read(), I want to connect to this database, get the corresponding products’s price and input it into my record. In the web I found many ways of creating new databases, but how do I open an EXISTING database and extract information using queries? Please, help me!

--- End quote ---

FPC has demo programs in source/packages/fcl-db/examples.
Lazarus has UI-based demos in  examples/database/.
The wiki has more information at https://wiki.freepascal.org/SQLite.

Dzandaa:
Hi,

I've created a little template to create a SQLite DB Skeleton.

I's not very fancy, but working.

Dezip it and put the folder "CreateDB" in your Templates directory (Tools->Project templates option) then in Lazarus:
File->New project from template->SQLite DB application

Fill the Dialog, with name and Path of your new program, main unit, search unit (data unit) main form, data form and Table name.

It's just a "contacts" DB with Firstname, Street, etc...

Not very clean, but good for an example

Hope this help.

B->


Handoko:
@Dzandaa
Nice work, thank you for sharing it.

----------

Dzandaa showed a fully working sqlite demo, which used TDBNavigator and other drag-and drop visual components. There is always more than one way to do the same thing. Here I want to show how it can be done fully using code (not drag-and-drop approach) and I tried to keep global variables usage as little as possible, so readers can follow the flow of the program easier.


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---unit Unit1; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls,  StdCtrls, SQLite3Conn, SQLDB; type   { TForm1 }   TForm1 = class(TForm)    btnCreate: TButton;    btnUse: TButton;    btnShow: TButton;    btnShowBack: TButton;    btnExact: TButton;    btnPartial: TButton;    btnAdd: TButton;    btnAddBack: TButton;    btnSave: TButton;    btnDelete: TButton;    edtProductID: TEdit;    edtProductName: TEdit;    edtProductPrice: TEdit;    lblProductID: TLabel;    lblProductName: TLabel;    lblProductPrice: TLabel;    mmoAllData: TMemo;    pnlAdd: TPanel;    pnlHome: TPanel;    pnlShow: TPanel;    procedure FormCreate(Sender: TObject);    procedure btnCreateClick(Sender: TObject);    procedure btnUseClick(Sender: TObject);    procedure btnShowClick(Sender: TObject);    procedure btnExactClick(Sender: TObject);    procedure btnPartialClick(Sender: TObject);    procedure btnAddClick(Sender: TObject);    procedure btnDeleteClick(Sender: TObject);    procedure btnAddBackClick(Sender: TObject);    procedure btnShowBackClick(Sender: TObject);    procedure edtProductIDExit(Sender: TObject);    procedure edtProductPriceExit(Sender: TObject);    procedure edtProductNameExit(Sender: TObject);    procedure btnSaveClick(Sender: TObject);  private    FActiveDatabaseFile: string;    function NoActiveDatabase: Boolean;  end; var  Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.FormCreate(Sender: TObject);const  Mono1 = 'Liberation Mono';  Mono2 = 'FreeMono';  Mono3 = 'Courier New';var  S: string;begin   FActiveDatabaseFile   := '';  Caption               := '';  Color                 := clGreen;  Height                := 256;  Width                 := 320;  pnlHome.BevelOuter    := bvNone;  pnlHome.Caption       := '';  pnlHome.Color         := $FFFFDD;  pnlShow.Caption       := '';  pnlShow.Color         := $DDFFDD;  pnlAdd.Caption        := '';  pnlAdd.Color          := $DDFFFF;  mmoAllData.ScrollBars := ssAutoBoth;   // Find an available monospaced font  S := 'default';  if Screen.Fonts.IndexOf(Mono1) >= 0 then    S := Mono1  else    if Screen.Fonts.IndexOf(Mono2) >= 0 then      S := Mono2    else      if Screen.Fonts.IndexOf(Mono3) >= 0 then        S := Mono3;  mmoAllData.Font.Name := S; end; procedure TForm1.btnCreateClick(Sender: TObject);var  Connection: TSQLite3Connection;  NewFile:    TSaveDialog;  S:          string;begin   // Ask for the filename  NewFile := TSaveDialog.Create(nil);  case NewFile.Execute of    True:      begin        S := NewFile.FileName;        NewFile.Free;      end;    False:      begin        NewFile.Free;        Exit;      end;  end;  if (ExtractFileExt(S)) = '' then    S := S + '.sqlite';  if FileExists(S) then  begin    ShowMessage('File already exists, process aborted.');    Exit;  end;  FActiveDatabaseFile := S;  Caption             := ExtractFileName(S);   // Create the database and tblProduct  Connection              := TSQLite3Connection.Create(nil);  Connection.DatabaseName := FActiveDatabaseFile;  try    Connection.CreateDB;    Connection.Transaction := TSQLTransaction.Create(nil);    Connection.StartTransaction;    try      Connection.ExecuteDirect(        'CREATE TABLE TBLPRODUCT '         +        '('                                +        '  PRODUCTID INTEGER PRIMARY KEY,' +        '  NAME CHAR(16) NOT NULL,'        +        '  PRICE REAL NOT NULL'            +        ');'        );      Connection.Transaction.Commit;    finally      Connection.Transaction.Free;    end;  finally    Connection.Free;  end; end; procedure TForm1.btnUseClick(Sender: TObject);var  UseFile: TOpenDialog;  S:       string;begin  UseFile := TOpenDialog.Create(nil);  case UseFile.Execute of    True:      begin        S := UseFile.FileName;        UseFile.Free;      end;    False:      begin        UseFile.Free;        Exit;      end;  end;  if not(ExtractFileExt(S) = '.sqlite') then  begin    ShowMessage('Only .sqlite files are supported.');    Exit;  end;  FActiveDatabaseFile := S;  Caption             := ExtractFileName(S);end; procedure TForm1.btnShowClick(Sender: TObject);var  Connection: TSQLite3Connection;  Query:      TSQLQuery;  S:          string;  i:          Integer;begin   if NoActiveDatabase then Exit;   mmoAllData.Clear;  for i := 260 downto 8 do  begin    pnlShow.Top := i;    if Odd(i) then Sleep(1);    Application.ProcessMessages;  end;   // Read, format and show the data  Connection              := TSQLite3Connection.Create(nil);  Connection.DatabaseName := FActiveDatabaseFile;  Connection.Transaction  := TSQLTransaction.Create(nil);  Connection.Open;  Connection.StartTransaction;  Query          := TSQLQuery.Create(nil);  Query.Database := Connection;  Query.SQL.Text := 'SELECT * FROM TBLPRODUCT;';  try    Query.Open;    while not(Query.EOF) do    begin      S := Format('%0:4D %1:-17S %2:7.2F',             [Query.FieldByName('PRODUCTID').AsInteger,             Query.FieldByName('NAME').AsString,             Query.FieldByName('PRICE').AsFloat]);      mmoAllData.Lines.Add(S);      Query.Next;    end;    Query.Close;    Connection.Transaction.Commit;  finally    Query.Free;    Connection.Transaction.Free;    Connection.Free;  end; end; procedure TForm1.btnExactClick(Sender: TObject);var  Connection: TSQLite3Connection;  Query:      TSQLQuery;  S:          string;begin   if NoActiveDatabase then Exit;  if not(InputQuery('Exact match search', 'Product ID', S)) then Exit;   // Do exact match search  Connection              := TSQLite3Connection.Create(nil);  Connection.DatabaseName := FActiveDatabaseFile;  Connection.Transaction  := TSQLTransaction.Create(nil);  Connection.Open;  Connection.StartTransaction;  Query          := TSQLQuery.Create(nil);  Query.Database := Connection;  Query.SQL.Text := 'SELECT * FROM TBLPRODUCT WHERE PRODUCTID = ''' + S + ''';';  S              := '';  try    Query.Open;    S := 'ProductID = ' + Query.FieldByName('PRODUCTID').AsString + LineEnding +         'Name = ' +  Query.FieldByName('NAME').AsString + LineEnding +         'Price = ' + Format('%.2F', [Query.FieldByName('PRICE').AsFloat]);    if Query.RecordCount <= 0 then      S := 'No result.';    Query.Close;    Connection.Transaction.Commit;  finally    Query.Free;    Connection.Transaction.Free;    Connection.Free;  end;  if not(S.IsEmpty) then    ShowMessage(S); end; procedure TForm1.btnPartialClick(Sender: TObject);var  Connection: TSQLite3Connection;  Query:      TSQLQuery;  S:          string;  i:          Integer;begin   if NoActiveDatabase then Exit;  if not(InputQuery('Exact match search', 'Name', S)) then Exit;   mmoAllData.Clear;  for i := 260 downto 8 do  begin    pnlShow.Top := i;    if Odd(i) then Sleep(1);    Application.ProcessMessages;  end;   // Do partial match search  Connection              := TSQLite3Connection.Create(nil);  Connection.DatabaseName := FActiveDatabaseFile;  Connection.Transaction  := TSQLTransaction.Create(nil);  Connection.Open;  Connection.StartTransaction;  Query          := TSQLQuery.Create(nil);  Query.Database := Connection;  Query.SQL.Text := 'SELECT * FROM TBLPRODUCT WHERE NAME LIKE ' + LineEnding +                    '''%' + S + '%'';';  try    Query.Open;    while not(Query.EOF) do    begin      S := Format('%0:4D %1:-17S %2:7.2F',             [Query.FieldByName('PRODUCTID').AsInteger,             Query.FieldByName('NAME').AsString,             Query.FieldByName('PRICE').AsFloat]);      mmoAllData.Lines.Add(S);      Query.Next;    end;    Query.Close;    Connection.Transaction.Commit;  finally    Query.Free;    Connection.Transaction.Free;    Connection.Free;  end; end; procedure TForm1.btnAddClick(Sender: TObject);var  i: Integer;begin  if NoActiveDatabase then Exit;  edtProductID.Text    := '';  edtProductName.Text  := '';  edtProductPrice.Text := '';  for i := 330 downto 8 do  begin    pnlAdd.Left := i;    if Odd(i) then Sleep(1);    Application.ProcessMessages;  end;end; procedure TForm1.btnDeleteClick(Sender: TObject);var  Connection: TSQLite3Connection;  Query:      TSQLQuery;  S:          string;  Found:      Boolean;begin   if NoActiveDatabase then Exit;  if not(InputQuery('Delete a record', 'Product ID', S)) then Exit;   Connection              := TSQLite3Connection.Create(nil);  Connection.DatabaseName := FActiveDatabaseFile;  Connection.Transaction  := TSQLTransaction.Create(nil);  Connection.Open;  Connection.StartTransaction;  Query          := TSQLQuery.Create(nil);  Query.Database := Connection;   // Check existence of the record  Query.SQL.Text := 'SELECT * FROM TBLPRODUCT WHERE PRODUCTID = ''' + S + ''';';  Found          := False;  try    Query.Open;    if Query.RecordCount > 0 then      Found := True;    Query.Close;    Connection.Transaction.Commit;    case Found of      True: // Delete the record        begin          Connection.ExecuteDirect('DELETE FROM TBLPRODUCT WHERE PRODUCTID = ' +                                   '''' + S + ''';');          Connection.Transaction.Commit;        end;      False:        ShowMessage('Item not found.');    end;  finally    Query.Free;    Connection.Transaction.Free;    Connection.Free;  end; end; procedure TForm1.btnAddBackClick(Sender: TObject);var  i: Integer;begin  for i := 8 to 330 do  begin    pnlAdd.Left := i;    if Odd(i) then Sleep(1);    Application.ProcessMessages;  end;end; procedure TForm1.btnShowBackClick(Sender: TObject);var  i: Integer;begin  for i := 8 to 260 do  begin    pnlShow.Top := i;    if Odd(i) then Sleep(1);    Application.ProcessMessages;  end;end; procedure TForm1.btnSaveClick(Sender: TObject);var  Connection: TSQLConnection;  i:          Integer;  r:          Real;begin   // Validate ProductID  if Length(edtProductID.Text) <= 0 then  begin    ShowMessage('ProductID cannot be empty.');    Exit;  end;  if not(TryStrToInt(edtProductID.Text, i)) then  begin    ShowMessage('Product ID must be number only.');    Exit;  end;  if (i < 1) or (i > 9999) then  begin    ShowMessage('Product ID must be >= 1 and <= 9999.');    Exit;  end;   // Validate ProductName  if Length(edtProductName.Text) <= 0 then  begin    ShowMessage('Name cannot be empty.');    Exit;  end;  if Length(edtProductName.Text) > 16 then  begin    ShowMessage('Name can have maximum 16 characters.');    Exit;  end;   // Validate ProductPrice  if not(TryStrToFloat(edtProductPrice.Text, r)) then  begin    ShowMessage('Price is not valid.');    Exit;  end;  if (r < 0) or (r > 9999) then  begin    ShowMessage('Price must be >= 0 and <= 9999.');    Exit;  end;   // Save the data  Connection              := TSQLite3Connection.Create(nil);  Connection.DatabaseName := FActiveDatabaseFile;  Connection.Transaction  := TSQLTransaction.Create(nil);  Connection.Open;  Connection.StartTransaction;  try    Connection.ExecuteDirect(      'INSERT INTO TBLPRODUCT '            +      '(PRODUCTID, NAME, PRICE) '          +      'VALUES ('                           +      '''' + edtProductID.Text    + ''', ' +      '''' + edtProductName.Text  + ''', ' +      '''' + edtProductPrice.Text + ''');'      );    Connection.Transaction.Commit;  finally    Connection.Transaction.Free;    Connection.Free;  end;   edtProductID.Text    := '';  edtProductName.Text  := '';  edtProductPrice.Text := ''; end; procedure TForm1.edtProductIDExit(Sender: TObject);begin  edtProductID.Text := Trim(edtProductID.Text);end; procedure TForm1.edtProductPriceExit(Sender: TObject);var  Price: Real;begin  if not(TryStrToFloat(edtProductPrice.Text, Price)) then Exit;   edtProductPrice.Text := Format('%.2F', [Price]);end; procedure TForm1.edtProductNameExit(Sender: TObject);begin  edtProductName.Text := Trim(edtProductName.Text);end; function TForm1.NoActiveDatabase: Boolean;begin  Result := False;  if not(FActiveDatabaseFile.IsEmpty) then Exit;  ShowMessage('No active database.' + LineEnding +              'Please create a new database or use an existing one.');  Result := True;end; end.

* Exact match search, see line #233.
* Partial match search, see line #272.
* "Garbage in, garbage out" ... it is always good to validate user inputs before saving them, see line #415 ... #454.
* Format function is very useful, see line #216.
* Simple way to find a monospace font, works on Ubuntu, Win7 and WinXP. See line #87 ... #96.
* Simple animation effect, see line #388 ... #393.For anyone who wants to learn Sqlite or SQL, I recommend these:
https://wiki.freepascal.org/SQLite
https://wiki.freepascal.org/SqlDBHowto
https://www.w3schools.com/sql/default.asp


--- Quote from: Evgenius on November 11, 2023, 11:10:30 pm ---... how do I open an EXISTING database and extract information using queries? Please, help me!

--- End quote ---

For database operations that do not return data, you use TConnection.ExecuteDirect, for example see line #140. For database operations that give data in return you use TQuery.SQL.Text, for example see line #211.

To read the related field of the return data, you use:
- TQuery.FieldByName(FieldName).AsString
- TQuery.FieldByName(FieldName).AsInteger
- TQuery.FieldByName(FieldName).AsFloat
- TQuery.FieldByName(FieldName).AsByte
- TQuery.FieldByName(FieldName).AsBoolean
- TQuery.FieldByName(FieldName).AsInteger

Before you can read the record you must call TQuery.Open, see line #213. For result that has several records, you can check it by using TQuery.EOF, see line #214. To move the the next record you call TQuery.Next, see line #221.

You have to prepare connection and transaction properly before starting any database operation, for example see line #457 ... #461. And if the operation need to use TQuery, you have create the query and link it to the database before using it, see line #209 .. #210. And don't forget to free all the objects after the operation, see line #223 .. #228.

Navigation

[0] Message Index

Go to full version