Difference between revisions of "SQLdb Tutorial1"

From Lazarus wiki
Jump to navigationJump to search
m
Line 20: Line 20:
 
But how can we now show the data from our database on the screen? First we need to inform IBConnection1, where the database employee.fdb is located (usually in the .../examples/empbuild/ subdirectory of your Firebird installation). Again you have the choice, if you want to use the object inspector to assign the path or if you want to do it directly in your source code. We choose to use the object inspector. Change the 'DatabaseName' property of IBConnection1 to the path to the employee.fdb file (e.g. C:\Program Files\Firebird\Firebird_2_0\examples\empbuild\EMPLOYEE.FDB). Before the database server is granting the access to the data, he will check the authorisation via username and password. A serious database application will ask the user for both values, when the application is started, to forward them to the server in the appropriate moment. To simplify matters we use the object inspector again. Change the 'UserName' property to 'SYSDBA' and 'Password' to 'masterkey'. To check, if all settings so far are correct, we can set the 'Connected' property to 'True'. If the path isn't correct or if username or password are wrong, you will get an error message. If the connection was successful, you should cut it now (set to 'False').
 
But how can we now show the data from our database on the screen? First we need to inform IBConnection1, where the database employee.fdb is located (usually in the .../examples/empbuild/ subdirectory of your Firebird installation). Again you have the choice, if you want to use the object inspector to assign the path or if you want to do it directly in your source code. We choose to use the object inspector. Change the 'DatabaseName' property of IBConnection1 to the path to the employee.fdb file (e.g. C:\Program Files\Firebird\Firebird_2_0\examples\empbuild\EMPLOYEE.FDB). Before the database server is granting the access to the data, he will check the authorisation via username and password. A serious database application will ask the user for both values, when the application is started, to forward them to the server in the appropriate moment. To simplify matters we use the object inspector again. Change the 'UserName' property to 'SYSDBA' and 'Password' to 'masterkey'. To check, if all settings so far are correct, we can set the 'Connected' property to 'True'. If the path isn't correct or if username or password are wrong, you will get an error message. If the connection was successful, you should cut it now (set to 'False').
  
Trotz der erfolgreichen Verbindung wurden aber immer noch keine Daten angezeigt. Das erklärt sich dadurch, daß wir dem Datenbank-Server auch noch nicht mitgeteilt haben, was er anzeigen soll. Die Datenbank employee.fdb enthält mehrere Tabellen. Wenn sie im Vorfeld die Struktur einer Datenbank nicht kennen, dann können sie auf Werkzeuge wie [http://www.flamerobin.org/index.php FlameRobin] zurückgreifen, um sich die Inhalte anzuzeigen. Aber auch Lazarus bietet ein Hilfsmittel - den DataDesktop. Sie finden ihn im /tools/lazdatadesktop/ Unterverzeichnis von Lazarus. Öffnen sie das Projekt lazdatadesktop.lpi und kompilieren sie es (setzt FPC 2.3.x voraus) (unser Beispielprojekt haben sie natürlich vorher gesichert).  
+
Although the connection was successful, there were no data displayed. The reason is simple. We haven't told to the database server, which data he should return. The database employee.fdb contains several tables. If you don't know the structure of a database, you can use tools like [http://www.flamerobin.org/index.php FlameRobin], to display the contents. Even Lazarus provides such a tool - the DataDesktop. You can find it in the /tools/lazdatadesktop/ subdirectory of Lazarus. Open the project lazdatadesktop.lpi and compile it (FPC 2.3.x is currently required) (of course you have saved our example project before).  
  
[[Image:DataDictonary1.png|framed|center|Der DataDesktop in Aktion]]
+
[[Image:DataDictonary1.png|framed|center|The DataDesktop in action]]
  
Zurück zu unserem Beispiel. Wir wollen alle Daten aus der Tabelle 'CUSTOMER' anzeigen. Der SQL Befehl dazu lautet:
+
Back to our example. We want to display all data from the table 'CUSTOMER'. The SQL instruction for that is:
 
  select * from CUSTOMER
 
  select * from CUSTOMER
Diesen Befehl müssen wir der 'SQL' Eigenschaft von SQLQuery1 zuweisen. Im Quelltext unseres Programms würde das so aussehen:
+
This instruction we have to assign to the 'SQL' property of SQLQuery1. In the source code of our project this would look like:
 
  SQLQuery1.SQL.Text := 'select * from CUSTOMER';
 
  SQLQuery1.SQL.Text := 'select * from CUSTOMER';
Wird der Befehl in Textform angegeben, dann muß er in einfache Anführungszeichen eingeschlossen werden. Man könnte natürlich auch den Inhalt einer anderen Komponente zuweisen.
+
The SQL instruction must be enclosed by single quotes. You also have the ability to assign the content of an other component (e.g. Edit1.Text).
  
Die Abfrage der Daten soll erfolgen, wenn der Benutzer auf den Button klickt. Mit einem Doppelklick auf Button1 erzeugt Lazarus das Grundgerüst der dafür notwendigen Prozedur. In ihrem Quelltext sollte jetzt folgendes stehen:
+
The request of the data should start, when the user clicks on the button. With a double click on Button1 Lazarus creates the skeleton of the necessary procedure. In our source code we should find the following lines:
  
 
<pascal>
 
<pascal>
Line 39: Line 39:
 
</pascal>
 
</pascal>
  
Zwischen ''begin'' und ''end'' müssen wir jetzt die Anweisungen eingeben, die zum Anzeigen der Daten erforderlich sind. Die 'SQL' Eigenschaft von SQLQuery1 kann nur geändert werden, wenn SQLQuery1 nicht aktiv ist. Daher schließen wir die Komponente zunächst:
+
Between ''begin'' and ''end'' we must now enter the instructions, which are needed to display the data. The 'SQL' property of SQLQuery1 can only be changed, if SQLQuery1 is not active. That's why we close the component first:
 
  SQLQuery1.Close;
 
  SQLQuery1.Close;
Da wir nicht sicher sein können, ob SQLQuery1 noch irgendwelche Daten enthält, rufen wir die 'Clear' Prozedur auf:
+
Because we can't be sure, if SQLQuery1 still contains any data, we call the 'Clear' procedure:
 
  SQLQuery1.SQl.Clear;
 
  SQLQuery1.SQl.Clear;
Dann weisen wir der 'SQL' Eigenschaft unsere SQL Anweisung zu:
+
Then we assign our SQL instruction to the 'SQL' property:
 
  SQLQuery1.SQL.Text := 'select * from CUSTOMER';
 
  SQLQuery1.SQL.Text := 'select * from CUSTOMER';
Jetzt müssen wir die Verbindung zur Datenbank herstellen sowie die Transaktion aktivieren und dann die Abfrage öffnen:
+
Now we need to establish the connection to the database, activating the transcation and open the query:
 
  IBConnection1.Connected := True;
 
  IBConnection1.Connected := True;
 
  SQLTransaction1.Active := True;
 
  SQLTransaction1.Active := True;
 
  SQLQuery1.Open;
 
  SQLQuery1.Open;
Die ersten beiden Anweisungen können auch weggelassen werden, da sie von der dritten Anweisung automatisch erledigt werden (dies gilt aber nicht im umgekehrten Fall beim Trennen der Verbindung). Wenn sie jetzt ihr Programm kompilieren würden, dann würden die Daten aus der Tabelle 'CUSTOMER' bereits angezeigt werden. Ein ordentliches Programm wird aber spätestens beim Beenden dafür sorgen, daß noch offene Verbindungen zur Datenbank geschlossen werden. Die Nebenwirkungen wären sonst nicht absehbar. Wir verwenden das OnClose Ereignis unseres Formulars (mittels Doppelklick im Objektinspektor erzeugen):
+
You can leave the first two instructions, because they are done automatically by the third instruction (but this will not happen in the contrary case, when you cut the connection). If you would now compile the project, you could already see the data from the table 'CUSTOMER'. But a serious application will take care at the latest, when the application is closed, that all open connections to the database will be closed. Else the secondary effects would not be foreseeable. We use the OnClose event of our form (create it with a double click in the object inspector):
  
 
<pascal>
 
<pascal>
Line 58: Line 58:
 
</pascal>
 
</pascal>
  
Beim Trennen verwenden wir die umgekehrte Reihenfolge wie beim Verbinden:
+
To close the connection we use the contrary oder as during the opening:
 
  SQLQuery1.Close;
 
  SQLQuery1.Close;
 
  SQLTransaction1.Active := False;
 
  SQLTransaction1.Active := False;
Line 65: Line 65:
 
=== Summary ===
 
=== Summary ===
  
Wir haben bisher gelernt, wie man mit dem SQLdb Package eine Verbindung zu einer Firebird Datenbank herstellt und die Daten einer Tablle am Bildschirm anzeigt.
+
Until now we have learned, how to connect to a Firebird database using the SQLdb package and how to display the content of a table on the screen.
  
 
== Enhancement of example 1 ==
 
== Enhancement of example 1 ==

Revision as of 21:38, 7 April 2009

Template:SQLdb Tutorial

Introduction

This tutorial has the intention to show the usage of the SQLdb Package on the basis of practical examples. It is primarily targeted at newbies. If somebody is looking for basics about databases and SQL, he should read the corresponding books. For this tutorial I use Firebird with the example database employee.fdb.

Thanks to Joost and Michael. Without their help this tutorial probably never has come about.

Requirements

If possible you should use a recent Lazarus version (with FPC 2.2.2 or newer) for this tutorial. If the SQLdb package isn't already installed, you should do it now (Components -> Installierte Packages einrichten ... -> SQLDBLaz 1.0). Furthermore you need Firebird (if possible version 2.0 or newer). The examples assume, that the standard settings (SYSDBA and masterkey) were not changed.

Example 1

First you should create a new Lazarus project. For the access to the database we need one TIBConnection, one TSQLTransaction and one TSQLQuery component from the 'SQLdb' tab in the component palette. The first component is specified for the access to Interbase or Firebird databases. The other two components can be used for all databases, which are supported by SQLdb. To display the data we use a TDBGrid component, which can be found on the 'Data Controls' tab. To connect this component with the database components we need a TDatasource component from the 'Data Access' tab. To trigger the actions we use a TButton from the 'Standard' tab. Now we have all components, that we need for the first example. You can enlarge the TDBGrid to have enough space to display all data.

Next we need to connect our components. A very simple way is to use the object inspector. But you also can do the connections in your source code. Change the 'Transaction' property of IBConnection1 to 'SQLTransaction1'. This cause also, that the 'Database' property of SQLTransaction1 is automatically changed to 'IBConnection1'. In the next step you should change the 'Database' property of SQLQuery1 to 'IBConnection1'. Lazarus automatically adds the value for the 'Transaction' property. Now we change the 'Dataset' property of Datasource1 to 'SQLQuery1'. Finally we change the 'Datasource' property of DBGrid1 to 'Datasource1'.

But how can we now show the data from our database on the screen? First we need to inform IBConnection1, where the database employee.fdb is located (usually in the .../examples/empbuild/ subdirectory of your Firebird installation). Again you have the choice, if you want to use the object inspector to assign the path or if you want to do it directly in your source code. We choose to use the object inspector. Change the 'DatabaseName' property of IBConnection1 to the path to the employee.fdb file (e.g. C:\Program Files\Firebird\Firebird_2_0\examples\empbuild\EMPLOYEE.FDB). Before the database server is granting the access to the data, he will check the authorisation via username and password. A serious database application will ask the user for both values, when the application is started, to forward them to the server in the appropriate moment. To simplify matters we use the object inspector again. Change the 'UserName' property to 'SYSDBA' and 'Password' to 'masterkey'. To check, if all settings so far are correct, we can set the 'Connected' property to 'True'. If the path isn't correct or if username or password are wrong, you will get an error message. If the connection was successful, you should cut it now (set to 'False').

Although the connection was successful, there were no data displayed. The reason is simple. We haven't told to the database server, which data he should return. The database employee.fdb contains several tables. If you don't know the structure of a database, you can use tools like FlameRobin, to display the contents. Even Lazarus provides such a tool - the DataDesktop. You can find it in the /tools/lazdatadesktop/ subdirectory of Lazarus. Open the project lazdatadesktop.lpi and compile it (FPC 2.3.x is currently required) (of course you have saved our example project before).

The DataDesktop in action

Back to our example. We want to display all data from the table 'CUSTOMER'. The SQL instruction for that is:

select * from CUSTOMER

This instruction we have to assign to the 'SQL' property of SQLQuery1. In the source code of our project this would look like:

SQLQuery1.SQL.Text := 'select * from CUSTOMER';

The SQL instruction must be enclosed by single quotes. You also have the ability to assign the content of an other component (e.g. Edit1.Text).

The request of the data should start, when the user clicks on the button. With a double click on Button1 Lazarus creates the skeleton of the necessary procedure. In our source code we should find the following lines:

<pascal> procedure TForm1.Button1Click(Sender: TObject); begin

end; </pascal>

Between begin and end we must now enter the instructions, which are needed to display the data. The 'SQL' property of SQLQuery1 can only be changed, if SQLQuery1 is not active. That's why we close the component first:

SQLQuery1.Close;

Because we can't be sure, if SQLQuery1 still contains any data, we call the 'Clear' procedure:

SQLQuery1.SQl.Clear;

Then we assign our SQL instruction to the 'SQL' property:

SQLQuery1.SQL.Text := 'select * from CUSTOMER';

Now we need to establish the connection to the database, activating the transcation and open the query:

IBConnection1.Connected := True;
SQLTransaction1.Active := True;
SQLQuery1.Open;

You can leave the first two instructions, because they are done automatically by the third instruction (but this will not happen in the contrary case, when you cut the connection). If you would now compile the project, you could already see the data from the table 'CUSTOMER'. But a serious application will take care at the latest, when the application is closed, that all open connections to the database will be closed. Else the secondary effects would not be foreseeable. We use the OnClose event of our form (create it with a double click in the object inspector):

<pascal> procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction); begin

end; </pascal>

To close the connection we use the contrary oder as during the opening:

SQLQuery1.Close;
SQLTransaction1.Active := False;
IBConnection1.Connected := False;

Summary

Until now we have learned, how to connect to a Firebird database using the SQLdb package and how to display the content of a table on the screen.

Enhancement of example 1

Wenn sie sich an die bisherigen Schritte gehalten haben, dann sollte ihr Quelltext etwa so aussehen: <delphi> unit Unit1;

{$mode objfpc}{$H+}

interface

uses

 Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
 IBConnection, sqldb, DBGrids, db, StdCtrls;

type

 { TForm1 }
 TForm1 = class(TForm)
   Button1: TButton;
   Datasource1: TDatasource;
   DBGrid1: TDBGrid;
   IBConnection1: TIBConnection;
   SQLQuery1: TSQLQuery;
   SQLTransaction1: TSQLTransaction;
   procedure Button1Click(Sender: TObject);
   procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
 private
   { private declarations }
 public
   { public declarations }
 end; 

var

 Form1: TForm1; 

implementation

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject); begin

 SQLQuery1.Close;
 SQLQuery1.SQL.Clear;
 SQLQuery1.SQL.Text:= 'select * from CUSTOMER';
 IBConnection1.Connected:= True;
 SQLTransaction1.Active:= True;
 SQLQuery1.Open;

end;

procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction); begin

 SQLQuery1.Close;
 SQLTransaction1.Active:= False;
 IBConnection1.Connected:= False;

end;

initialization

 {$I unit1.lrs}

end. </delphi>

Nun wird man aber in den seltensten Fällen den kompletten Inhalt einer Tabelle benötigen. Nehmen wir an, es sollen nur die Kunden aus den USA angezeigt werden. Dafür müßte die SQL Anweisung wie folgt lauten:

select * from CUSTOMER where COUNTRY = 'USA'

Diese Anweisung werden wir aus zwei Gründen nicht für unser Beispielprogramm verwenden: Zum einen gibt es ein Problem bei der Verwendung des einfachen Anführungszeichens. Der Compiler würde das Anführungszeichen vor USA als schließendes Zeichen betrachten (das erste Zeichen steht ja vor select from...) und damit die SQL Anweisung ungültig machen. Der zweite und gewichtigere Grund ist die Tatsache, daß bei der Entwicklung des Programms noch gar nicht bekannt ist, welche Einschränkung der Anzeige später vorgenommen werden soll. Der Benutzer soll in seiner Flexibilität nicht eingeschränkt werden. Dazu ersetzen wir zunächst 'USA' durch einen Platzhalter:

select * from CUSTOMER where COUNTRY = :COUNTRY

Die Platzhalter sind durch den führenden Doppelpunkt gekennzeichnet. Um dem Benutzer die Eingabe des Filterwertes zu ermöglichen, platzieren sie eine TEdit Komponente (Seite 'Standard' in der Komponentenpalette) auf ihrem Formular. Löschen sie den Wert der 'Text' Eigenschaft. Über die 'Params' Eigenschaft von TSQLQuery können wir nun mit dem in TEdit eingegebenem Text unseren Platzhalter ersetzen:

SQLQuery1.Params.ParamByName('COUNTRY').AsString := Edit1.Text;

Der Parameter kann sowohl über seine Position als auch seinen Namen spezifiziert werden. Letzteres dürfte vor allem die Lesbarkeit des Quelltextes verbessern. Insgesamt sollte die Prozedur jetzt so aussehen: <delphi> procedure TForm1.Button1Click(Sender: TObject); begin

 SQLQuery1.Close;
 SQLQuery1.SQL.Clear;
 SQLQuery1.SQL.Text:= 'select * from CUSTOMER where COUNTRY = :COUNTRY';
 SQLQuery1.Params.ParamByName('COUNTRY').AsString := Edit1.Text;
 IBConnection1.Connected:= True;
 SQLTransaction1.Active:= True;
 SQLQuery1.Open;

end; </delphi> Sie können jetzt ruhig mit den Einstellungen ein wenig herumspielen. Wenn sie einen Filterwert eingeben, der in der Datenbank nicht vorhanden ist, dann wird eine leere Tabelle angezeigt. Es können jedoch auch ernsthafte Probleme auftreten. Da bei einer Client-Server-Anwendung üblicherweise Client und Server räumlich getrennt sind, ist meist nicht auf den ersten Blick ersichtlich, warum es zu einem Problem gekommen ist. Ist der Server heruntergefahren worden oder hat nur jemand einen Stecker gezogen? Zugriffe auf eine Datenbank sollten daher immer in eine try ... except und/oder try ... finally Schleife eingebunden werden. Nur so ist sichergestellt, daß Datenbankfehler abgefangen werden können und der Anwender nicht im Regen stehengelassen wird. Für unsere Beispielanwendung könnte eine rudimentäre Behandlungsroutine wie folgt aussehen: <delphi> begin

 try
   SQLQuery1.Close;
   ...
   SQLQuery1.Open;
 except
   on EDatabaseError do
   begin
     MessageDlg('Fehler','Es ist ein Datenbankfehler aufgetreten.',mtError,[mbOK],0);
     Edit1.Text:=;
   end;
 end;

end; </delphi> Hier wird jedoch nur eine einfache Meldung ausgegeben.

See also