SqlDBHowto/de

From Lazarus wiki
Revision as of 15:59, 24 March 2012 by Vincent (talk | contribs) (Text replace - "delphi>" to "syntaxhighlight>")

Deutsch (de) English (en) español (es) français (fr) Nederlands (nl) 中文(中国大陆)‎ (zh_CN)

Einleitung

This page is a translation of SqlDBHowto/nl. The Dutch text is leading, even though I took the liberty to make some corrections (minor corrections in my opinion.)

This text is setup as a 'how-to'. I want to answer a number of questions one by one, explain how you can use the various classes. All those questions are put one after the other and form a sort of tutorial.

I will try to word it in such a way that the text can be used for Lazarus as well as Freepascal. However, the examples are for Freepascal (i.e. they are console applications.)

Verbinden mit einem Datenbankserver

SqlDB verbindet nicht direkt mit einem Datenbankserver sondern verwendet einen Client, der dem verwendeten Server entspricht. SqlDB sendet die Kommandos an die Client-Bibliothek, die sich mit der Datenbank verbindet und die Kommandos übergibt. Das bedeutet, dass eine Client-Bibliothek auf dem Computer installiert sein muss, um eine Verbindung zur Datenbank herzustellen. Unter Windows ist ein Client üblicherweise eine .dll Datei, unter Linux eine .so und unter OS/X eine .dylib Datei.

When the client library is installed properly you can connect to a database server using a TSQLConnection component. Various TSQLConnection components are available for different database servers. For example, use TIBConnection to connect to a Firebird/Interbase database; TPQConnection for PostgreSQL and TMySQL40Connection, TMySQL41Connection and TMySQL50Connection for MySQL databases with version number 4.0, 4.1 and 5.0 respectively. The differences between the MySQL client versions are big, to the extend that the clients and connections cannot be interchanged. If a MySQL client library version 4.1 is installed, you have to use a TMySQL41Connection. This is not related to the MySQL server.

Obwohl die Details für die verschiedenen Datenbanken differieren, muss man im allgemeinen nur vier Eigenschaften setzen für die Verbindung zum Datenbankserver: den Namen der Datenbank, den Server-Namen oder die IP Adresse, den Benutzernamen und das Passwort. Wenn diese Eigenschaften gesetzt sind, können sie eine Verbindung mit der 'open' Methode herstellen. Wenn die Verbindung fehlschlägt, wird eine EDatabaseError Exception ausgelöst. Verwenden sie die 'connected' Eigenschaft um zu testen, ob die Verbindung zum Datenbank-Server hergestellt wurde. Mit der 'close' Methode beenden sie die Verbindung zum Server.

 Program ConnectDB
 
 var AConnection : TSQLConnection;
 
 Procedure CreateConnection;
 begin
   AConnection := TIBConnection.Create(nil);
   AConnection.Hostname := 'localhost';
   AConnection.DatabaseName := '/opt/firebird/examples/employee.fdb';
   AConnection.UserName := 'sysdba';
   AConnection.Password := 'masterkey';
 end;
 
 begin
   CreateConnection;
   AConnection.Open;
   if Aconnection.Connected then
     writeln('Erfolgreich verbunden!')
   else
     writeln('This is not possible, because if the connection failed, ' +
               'an exception should be raised, so this code would not ' +
                'be executed');
   AConnection.Close;
   AConnection.Free;
 end.

If an exception is thrown, read the error message carefully. It maybe the database server is not running, the user name or password is incorrect or the database name or ip address are typed incorrectly. If the error message states that the client library cannot be found, then check if the client is installed correctly. Often the error message states literaly the name of the file looked for.

Abfragen direkt ausführen / Eine Tabelle erstellen

SQLdb - der Name sagt es bereits - funktioniert nur mit Datenbankservern, die Gebrauch von SQL machen. SQL steht für 'Structured Query Language'. SQL ist eine Sprache die entwickelt wurde, um das Arbeiten mit relationalen Datenbanken zu ermöglichen. Jedes Datenbanksystem benutzt einen eigenen Dialekt. Aber die überwiegende Anzahl von SQL Anweisungen ist für alle Datenbanksysteme gleich. Man unterscheidet zwischen Anweisungen, die Informationen zurückgeben, und Anweisungen, die keine Informationen zurückgeben. Wenn sie die Informationen nutzen wollen, die von einer SQL Anweisung zurückgegeben werden, dann müssen die TSQLQuery Komponente verwenden (siehe Daten aus einer Tabelle lesen.) If you do not expect to use the information returned by the SQL statement, then you may also use the 'ExecuteDirect' method of a TSQLConnection.

Die meisten Datenbanksysteme führen SQL Anweisungen innerhalb einer Transaktion aus. If you want changes made within a transaction available in other transactions, or have those changes available even after closing the transaction, then you have to 'commit' the transaction. To support transactions Sqldb contains the TSQLTransaction component. A SQL statement that is executed by Sqldb must always be executed within a transaction. Even if the database system does not support transactions. Also, there are database sustems that do support transaction for which TSQLConnection does not (yet) support transaction. Even then, you must use the TSQLTransaction component.

Um TSQLConnection.ExecuteDirect zu verwenden um eine SQL Anweisung auszuführen, müssen sie angeben, welche Transaktion verwendet werden soll. In turn, to use TSQLTransaction you must specify which 'Connection' must be used.

Das folgende Beispiel erstellt eine Tabelle 'TBLNAMES' mit den Feldern 'NAME' und 'ID' und fügt zwei Datensätze ein. Die verwendeten SQL Anweisungen sind nicht erläutert. Für weitere Informationen über die SQL Anweisungen, ihre Verwendung und Schreibweise schauen sie bitte in der Dokumentation ihres Datenbanksystems nach. Die Prozedur 'CreateConnection' ist im Code-Beispiel im Abschnitt Verbinden mit einem Datenbankserver definiert.

program CreateTable;

var AConnection : TSQLConnection;
    ATransaction : TSQLTransaction;

procedure CreateTransaction;
begin
  ATransaction := TSQLTransaction.Create;
  ATransaction.Database := AConnection;
end;

begin
  CreateConnection;
  CreateTransaction;
  AConnection.Transaction := ATransaction;
  AConnection.Open;
  ATransaction.StartTransaction;
  AConnection.ExecuteDirect('create table TBLNAMES (ID integer, NAME varchar(40));'); 
  
  // Some database-server types need a commit before you can use a newly created table. (Firebird)
  // With .Commit you also close the transaction
  ATransaction.Commit; 

  ATransaction.StartTransaction;
  AConnection.ExecuteDirect('insert into TBLNAMES (ID,NAME) values (1,'Name1');'); 
  AConnection.ExecuteDirect('insert into TBLNAMES (ID,NAME) values (2,'Name2');'); 
  ATransaction.Commit; 
  AConnection.Close;
  AConnection.Free;
  ATransaction.Free;
end.

Daten aus einer Tabelle lesen

Verwenden sie die TSQLQuery Komponente, um Daten aus einer Tabelle zu lesen. Eine TSQLQuery Komponente muss mit einer TSQLConnection und einer TSQLTransaction Komponente verbunden werden, damit sie funktionieren kann. Setting the TSQLConnection and TSQLTransaction is discussed in #How to connect to a database server? and #How to execute direct queries/make a table?.

Wenn TSQLConnection, TSQLTransaction und TSQLQuery verbunden sind, dann werden weitere Einstellungen für TSQLQuery benötigt. TSQLQuery besitzt eine 'SQL' Eigenschaft, die ein TStrings Objekt enthält. Die 'SQL' Eigenschaft enthält eine SQL Anweisung, die ausgeführt werden muss. Wenn alle Daten aus einer Tabelle gelesen werden sollen, dann setzten sie die 'SQL' Eigenschaft auf 'SELECT * FROM tablename;'. Verwenden sie 'open', um die Tabelle auf dem Server auszulesen und die Daten im TSQLQuery dataset abzulegen. Auf die Daten kann über TSQLQuery zugegriffen werden, bis diese mittels 'close' geschlossen wird. TSQLQuery ist eine Unterklasse von TDataset. TDataset besitzt eine 'Fields' Kollektion, die alle Spalten der Tabelle enthält. Das TDataset behält auch den aktuellen Datensatz im Auge. Verwenden sie 'First', 'Next', 'Prior' und 'Last', um den aktuellen Datensatz zu ändern. 'Bof' gibt 'True' zurück, wenn der erste Datensatz erreicht ist und 'Eof' gibt 'True' zurück, wenn der letzte Datensatz erreicht ist. Um den Wert eines Feldes im aktuellen Datensatz zu lesen müssen sie zunächst das richtige 'TField' Objekt herausfinden und dann 'AsString', 'AsInteger', etc. verwenden.


Below is an example that displays all values of the table as it was made in #How to execute direct queries/make a table? above.

 Program ShowData;
 
 var AConnection : TSQLConnection;
     ATransaction : TSQLTransaction;
 
 procedure GetQuery : TSQLQuery;
 var AQuery : TSQLQuery;
 begin
   AQuery := TSQLQuery.Create;
   AQuery.Database := FConnection;
   AQuery.Transaction := FTransaction;
   Result := AQuery;
 end;
 
 var Query : TSQLQuery;
 
 begin
   CreateConnection;
   CreateTransaction;
   Query := GetQuery;
   Query.SQL.Text := 'select * from tblNames';
   AConnection.Open;
   Query.Open;
   while not Query.Eof do
     begin
     Writeln('ID: ', Query.FieldByName('Name').AsInteger, 'Name: ' +
                                   Query.FieldByName('Name').AsString);
     Query.Next;
     end;
   Query.Close;
   AConnection.Close;
   Query.Free;
   ATransaction.Free;
   AConnection.Free;
 end.

(Dieser Code ist natürlich noch nicht komplett. So fehlen z.B. 'try...finally' Blöcke. However, the above code intends to show the database code and thus the finishing touches are left out.) Beachten sie, dass 'TSQLTransaction.StartTransaction' nicht verwendet wird. Das ist nicht notwendig. Wenn TSQLQuery geöffnet wird, wird die SQL Anweisung ausgeführt und wenn keine Transaktion verfügbar ist, dann wird eine Transaktion automatisch gestartet. Der Programmierer muss die Transaktion nicht explizit starten. Das selbe gilt für die von TSQLConnection gehaltene Verbindung. Die Verbindung wird bei Bedarf geöffnet. Die Zeile 'Aconnection.Open' wird nicht wirklich benötigt. Wenn eine TSQLTransaction gelöscht wird, dann wird ein automatischer 'rollback' ausgeführt. Änderungen an den Daten im Rahmen der Transaktion gehen verloren.

Warum gibt TSQLQuery.RecordCount immer 10 zurück?

Um die Datensätze in einer Datenbank zu zählen verwenden sie '.RecordCount'. Jedoch zeigt '.RecordCount' die Anzahl von Datensätzen, die bereits vom Server geladen sind. Sqldb liest (per Voreinstellung) nicht alle Datensätze ein, wenn TSQLQuery geöffnet wird, sondern nur die ersten 10. Erst wenn auf den elften Datensatz zugegriffen wird, werden die nächsten 10 Datensätze geladen. Durch die Verwendung von '.Last' werden alle Datensätze geladen.

Wenn sie die tatsächliche Anzahl von Datensätzen auf dem Server wissen müssen, dann rufen sie erst '.Last' auf und dann '.RecordCount'. Es gibt aber auch eine Alternative. Die Anzahl von Datensätzen, die der Server zurückgibt, wird mit der '.PacketRecords' Eigenschaft festgelegt. Der Vorgabewert ist 10. Wenn sie den Wert auf -1 setzen, dann werden alle Datensätze auf einmal geladen.

Lazarus

Lazarus besitzt verschiedene Komponenten, um Daten von einem TDataset auf einem Formular anzuzeigen. Instead of a While-loop and Writeln statements as use avobe, you can use the components to show the data in a table. Place the right TSQLConnection, TSQLTransaction and TSQLQuery components on a form, then connect them and set them properly. In addition you will need a TDatasource, set to 'TDatasource.Dataset' property to the TSQLQuery component you used. (Note do not set the 'TSQLQuery.Datasource' property to the TDatasource compnent you used. The 'TSQLQuery.Datasource' property is only used only in master-detial tables.) Subsequently you may put a TDBGrid onto the form and set the 'Datasource' property of the grid to the TDatasource component you added before.

To see if it all works, set the 'Connected' property of the TSQLConnection to 'True' in the Lazarus IDE. The IDE will try to connect to the database server immediately. If this works you can set the 'TSQLQuery.Active' property to 'True'. If everything is right, you will see - within the IDE - all data from the table immediately on the screen.

Daten in einer Tabelle ändern

Um die Daten in einem Datensatz zu bearbeiten, muss TDataset (von dem TSQLQuery abgeleitet ist) in den Bearbeitungsmodus versetzt werden. Dies kann durch Aufrufen der '.Edit', '.Insert' oder '.Append' Methoden geschehen. Verwenden sie die '.Edit' Methode, um den aktuellen Datensatz zu ändern. Verwenden sie '.Insert', um einen neuen Datensatz an der aktuellen Position einzufügen. Verwenden sie '.Append', um einen neuen Datensatz am Ende der Tabelle einzufügen. Im Bearbeitungsmodus können sie die Werte der Felder mittels der 'Fields' Eigenschaft ändern. Use 'Post' to validate the new data, if the data is valid then the edit mode is left. If you move to another record - for example by using '.Next' - and the dataset is in edit mode, then first '.Post' is called. Verwenden sie '.Cancel', um alle Änderungen seit dem letzten Aufruf von '.Post' zu verwerfen und den Bearbeitungsmodus zu verlassen.

Query.Edit;
Query.FieldByName('NAME').AsString := 'Edited name';
Query.Post;

The above is not the complete story yet. TSQLQuery is derived from TBufDataset which makes use of buffered updates. Buffered update mean that after you called 'Post' the changes in the dataset are visible immediately, but they are not sent to the database server. What does happen is that the changes are maintained in a change log. When the '.ApplyUpdates' method is called, then all changes in the change log are sent to the database. Nur dann erlangt der Datenbankserver Kenntnis von allen Änderungen. Die Änderungen werden an den Server gesendet innerhalb einer Transaktion von TSQLTransaction. Stellen sie sicher, dass die richtige Transaktion eingestellt ist, bevor 'ApplyUpdates' aufgerufen wird. After applying the updates, a commit must be executed to publish or store the changes.

The below is an example of changing the data in a table, sending the changes to the server and comitting the transaction.

Program EditData;

var AConnection : TSQLConnection;
    ATransaction : TSQLTransaction;
    Query : TSQLQuery;

begin
  CreateConnection;
  CreateTransaction;
  AConnection.Transaction := ATransaction;
  Query := GetQuery;
  Query.SQL.Text := 'select * from tblNames';
  Query.Open;
  Query.Edit;
  Query.FieldByName('NAME').AsString := 'Edited name';
  Query.Post;
  Query.UpdateMode := upWhereAll;
  Query.ApplyUpdates;
  AConnection.Transaction.Commit;
  Query.Free;
  ATransaction.Free;
  AConnection.Free;
end.

For a discussion of 'UpdateMode' continue reading.

Wie Sqldb Änderungen an den Datenbankserver sendet

In dem Code-Beispiel in Daten in einer Tabelle ändern finden sie die Zeile

  Query.UpdateMode := upWhereAll;

ohne Erläuterung, was dieser Befehl macht. Der beste Weg es herauszufinden besteht darin, die Zeile wegzulassen. Wenn sie die Anweisung weglassen und sich ansonsten genau an den Beispielcode halten, dann erhalten sie die folgende Fehlermeldung:

No update query specified and failed to generate one. (No fields for inclusion in where statement found)

Um zu verstehen, was schief gegangen ist, müssen sie wissen, wie Änderungen an den Datenbankserver gesendet werden. The only way to get data in a SQL server is by executing SQL queries. SQL has three types of queries for three different ways of manupulating a record. To create a new record, change or delete a record insert, update and delete statements are executed respectively. An update statement may be as follows:

update TBLNAMES set NAME='Edited name' where ID=1;

To send a change to the database server, Sqldb must assemble an update query. To assemble the query, three things are needed:

Der Name der Tabelle 
The table name is retrieved from parsing the select query, although this doesn't always work.
UPDATE oder INSERT clause 
Diese enthält die Felder, die geändert werden sollen.
WHERE clause 
Diese enthält die Felder, die bestimmen, welche Datensätze geändert werden sollen.

Jedes Feld (jedes TField in Fields) besitzt eine ProviderFlags Eigenschaft. Nur Felder mit pfInUpdate in ProviderFlags werden in dem update oder insert Abschnitt einer query verwendet. Vorgabemäßig ist pfInUpdate bei allen Feldern in deren ProviderFlags Eigenschaft gesetzt.

Which fields are used in the WHERE clause depends on the UpdateMode property of the query and the ProviderFlags property of the fields. Fields with pfInkey in their ProviderFlags are always used in the WHERE clause. A field will have the pfInKey flag set automatically if the field is part of the primary key of the table and 'TSQLQuery.UsePrimaryKeyAsKey' returns 'True'.

The default value for UpdateMode of the query is upWhereKeyOnly. In this update mode only fields with pfInkey in their ProviderFlags property are used in the WHERE clause. If none of the fields have their pfInKey flag set, then no fields are available for the WHERE clause and the error message from the beginning of this section will be returned. Sie können das Problem lösen durch:

* Hinzufügen eines Primärschlüssels zur Tabelle und setzen von TSQLQuery.UsePrimaryKeyAsKey auf 'True', oder
* Setzen des pfInkey Flags für ein oder mehrere Felder im Code.

Die UpdateMode Eigenschaft kennt zwei weitere mögliche Werte. 'upWhereAll' can be used to add all fields with the 'pfInWhere' flag set to the WHERE clause. By default all fields have this flag set. 'upWhereChanged' can be used to add only those fields that have the 'pfInWhere' flag set and that are changed in the current record.

How to execute a query using TSQLQuery?

Neben den Anweisungen, die Daten zurückgeben (siehe #How to read data from a table?), gibt es bei SQL Anweisungen, die keine Daten zurückgeben, zum Beispiel INSERT, UPDATE und DELETE. Diese Anweisungen können mittels TSQLConnection.ExecuteDirect ausgeführt werden, aber TSQLQuery kann ebenso verwendet werden. Wenn keine Daten zurückgegeben werden, dann verwenden sie TSQLQuery.ExecSQL anstatt TSQLQuery.Open. Use TSQLQuery.Open to open the dataset returned by the SQL statement.

Die folgende Prozedur erzeugt eine Tabelle mittels TSQLQuery und fügt zwei Datensätze ein.

 procedure CreateTable;
 
 var Query : TSQLQuery;
 
 begin
   Query := GetQuery;
   Query.SQL.Text := 'create table TBLNAMES (ID integer, NAME varchar(40));';
   Query.ExecSQL;
 
   Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (1,'Name1');';
   Query.ExecSQL;
 
   Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (2,'Name2');';
   Query.ExecSQL;
 
   Query.Close;
   Query.Free;
 end;

Verwendung von Parametern in einer Abfrage

Im Code-Beispiel von #How to execute a query using TSQLQuery? wird die selbe Abfrage zweimal verwendet, nur mit anderen einzusetzenden Werten. Eine bessere Möglichkeit dies zu erledigen ist die Verwendung von Parametern in der Abfrage.

Die Syntax von Parametern in Abfragen ist je nach verwendeter Datenbank unterschiedlich. Aber diese Unterschiede können von TSQLQuery gehandhabt werden. Ersetzen sie die Werte in der Abfrage mit einem Doppelpunkt gefolgt von dem Namen des Parameters, den sie verwenden wollen. Zum Beispiel:

 Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (:ID,:NAME);';

Diese Abfrage enthält zwei Parameter: 'ID' und 'NAME'. Um die Parameter zu bestimmen, wird die Abfrage in dem Moment analysiert, wenn der Text von TSQLQuery.SQL zugewiesen oder geändert wird. Alle existierenden Parameter werden entfernt und die neuen Parameter zur 'TSQLQuery.Params' Eigenschaft hinzugefügt. Einem Parameter einen Wert zuzuweisen ist ähnlich der Zuweisung eines Werts zu einem Feld im Dataset:

 Query.Params.ParamByName('Name').AsString := 'Name1';

Sie können der Abfrage mitteilen, welche Art von Daten in dem Parameter gespeichert werden sollen. Der Datentyp des Parameters wird in dem Moment bestimmt, wenn ein Wert das erste Mal dem Parameter zugewiesen wird. Durch die Zuweisung eines Wertes mittels '.AsString', erhält der Parameter den Datentyp 'ftString'. Sie können den Datentyp auch direkt über die 'DataType' Eigenschaft bestimmen. Wenn dem Parameter ein falscher Datentyp zugewiesen wird, dann werden Probleme auftauchen, wenn die Abfrage geöffnet bzw. ausgeführt wird.

Das folgende Beispiel erzeugt die selbe Tabelle wie im vorherigen Beispiel, jetzt aber unter Verwendung von Parametern:

 procedure CreateTableUsingParameters;
 
 var Query : TSQLQuery;
 
 begin
   Query := GetQuery;
   Query.SQL.Text := 'create table TBLNAMES (ID integer, NAME varchar(40));';
   Query.ExecSQL;
 
   Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (:ID,:NAME);';
 
   Query.Params.ParamByName('ID').AsInteger := 1;
   Query.Params.ParamByName('ID').AsString := 'Name1';
   Query.ExecSQL;
 
   Query.Params.ParamByName('ID').AsInteger := 2;
   Query.Params.ParamByName('ID').AsString := 'Name2;
   Query.ExecSQL;
 
   Query.Close;
   Query.Free;
 end;

Beachten sie, dass dieses Beispiel mehr Quelltext benötigt als das Beispiel ohne Parameter. Was ist dann der Vorteil der Verwendung von Parametern? Geschwindigkeit ist einer der Punkte. Das Beispiel mit Parametern wird schneller ausgeführt, da die Abfrage nur einmal analysiert werden muss. TSQLQuery analysiert die Abfrage zwar nur einmal, aber auch der Datenbankserver analysiert die Abfrage nur einmal. Die meisten Datenbanksysteme unterstützen Parameter. Whenever a query is used more than once with different values for the parameter each time, then the database server only parses the query and plans the query only once making execution considerably faster. Use 'TSQLQuery.Prepare' to determine the moment the query is parsed and planned by the database server. Use 'TSQLQuery.UnPrepare' to make sure the query is parsed and planned every time by the data server.

Ein anderer Grund für die Verwendung von prepared statements ist die Vermeidung von SQL-Injection. Aber in manchen Fällen vereinfacht es auch nur die Programmierung.

Siehe auch

* Working_With_TSQLQuery.