Database tutorial SQLdb1/de

From Free Pascal wiki
Revision as of 12:57, 31 August 2011 by Billyraybones (talk | contribs) (updated up to "Choosing what data to show")
Jump to navigationJump to search

Template:SQLdb Tutorial

Einleitung

Dieses Tutorial hat das Ziel, die Verwendung des SQLdb Packages anhand praktischer Beispiele zu demonstrieren. Es richtet sich vor allem an Einsteiger. Wer nach Grundlageninformationen über Datenbanken und SQL sucht, dem seien die einschlägigen Bücher empfohlen. Für das Tutorial greife ich auf Firebird mit der Beispieldatenbank employee.fdb zurück. Andere Datenbanksysteme können ebenfalls eingesetzt werden. Die dafür nötigen Anpassungen sind im Text erläutert.

Bedanken möchte ich mich bei Joost und Michael, ohne deren Hilfe das Tutorial wahrscheinlich nicht zustande gekommen wäre.

Voraussetzungen

Für dieses Tutorial sollten Sie möglichst eine aktuelle Lazarus Version verwenden (mit FPC 2.2.2 oder höher). Sollte das SQLdb Package noch nicht installiert sein, dann holen Sie das jetzt nach (Package -> Installierte Packages einrichten ... -> SQLDBLaz 1.0.1).

Außerdem wird Firebird (möglichst Version 2.0 oder höher) benötigt. Die Beispiele gehen davon aus, dass die Standardvorgaben (SYSDBA und masterkey) nicht verändert wurden und dass Sie die Beispielsdatenbank "employee" installiert haben.

Falls Sie ein anderes Datenbanksystem (z.B. MySQL, PostgreSQL, Oracle, SQLite oder eine andere ODBC-Datenbank) einsetzen: Sie müssen die richtige Datenbank-/Tabellenstruktur haben (siehe unten) und den entsprechenden TSQLConnector-Nachfolger (siehe ebenfalls unten). Falls Ihre Datenbank signifikante Unterschiede bezüglich Transaktionen u.s.w. aufweist, machen Sie bitte eine Anmerkung im betreffenden Abschnitt.

Firebird Datenbankbibliotheken unter Windows

Zuletzt brauchen wir unter Windows noch die Firebird Client DLLs, wahlweise in Ihrem System-Verzeichnis, aber vorzugsweise in Ihrem Projekt-Verzeichnis (für die Unterstützung durch die IDE zur Entwurfszeit) und im Ausgabeverzeichnis wo das ausführbare Programm ist (zum Ausführen). Ein einfacher Weg, sich die Client DLLs zu besorgen: downloaden Sie Firebird Embedded 2.5 von [1] Extrahieren Sie diese Dateien in Ihr Anwendungsverzeichnis: <bash> fbembed.dll firebird.msg ib_util.dll icudt30.dll icuin30.dll icuuc30.dll Microsoft.VC80.CRT.manifest msvcp80.dll msvcr80.dll </bash> Benennen Sie "fbembed.dll" um in "fbclient.dll" (der Name für einen regulären Client-Server Firebird-Klienten). Die Embedded-Firebird DLL kann auch als regulärer Firebird-Klient agieren.

Zuletzt kompilieren Sie Ihr Projekt (sogar wenn es leer ist) einmal, um das Ausgabeverzeichnis zu erzeugen, und kopieren Sie die DLLs in dieses Verzeichnis.

Firebird Datenbankbibliotheken unter anderen Systemen

Unter Linux/OSX werden Sie ebenfalls die gemeinsamen Firebird-Klientenbibliotheken brauchen. Benützen Sie unter Linux die Methoden Ihrer Distribution zum Holen von Programmen und besorgen Sie sich die Firebird-Klienten-Packages, z.B. unter Debian: <bash> aptitude install libfbclient2 </bash>

Keine employee.fdb installiert?

Falls Sie die Beispielsdatenbank "employee" nicht installiert haben, oder ein unterschiedliches Datenbanksystem benutzen, ist hier eine Minimalversion der von uns verwendeten Tabelle: <sql> CREATE TABLE CUSTOMER (

 CUST_NO CUSTNO NOT NULL,
 CUSTOMER VARCHAR(25) NOT NULL,
 CITY VARCHAR(25),
 COUNTRY VARCHAR(15),
 CONSTRAINT INTEG_60 PRIMARY KEY (CUST_NO)

); </sql> Ein paar Daten, damit Sie zumindest irgendetwas anzeigen können: <sql> INSERT INTO CUSTOMER (CUST_NO, CUSTOMER, CITY, COUNTRY) VALUES ('2001', 'Michael Design', 'San Diego', 'USA'); INSERT INTO CUSTOMER (CUST_NO, CUSTOMER, CITY, COUNTRY) VALUES ('2002', 'VC Technologies', 'Dallas', 'USA'); INSERT INTO CUSTOMER (CUST_NO, CUSTOMER, CITY, COUNTRY) VALUES ('2003', 'Klämpfl, Van Canneyt and Co.', 'Boston', 'USA'); INSERT INTO CUSTOMER (CUST_NO, CUSTOMER, CITY, COUNTRY) VALUES ('2004', 'Felipe Bank', 'Manchester', 'England'); INSERT INTO CUSTOMER (CUST_NO, CUSTOMER, CITY, COUNTRY) VALUES ('2005', 'Joost Systems, LTD.', 'Central Hong Kong', 'Hong Kong'); INSERT INTO CUSTOMER (CUST_NO, CUSTOMER, CITY, COUNTRY) VALUES ('2006', 'Van der Voort International', 'Ottawa', 'Canada'); INSERT INTO CUSTOMER (CUST_NO, CUSTOMER, CITY, COUNTRY) VALUES ('2007', 'Mrs. Mauvais', 'Pebble Beach', 'USA'); INSERT INTO CUSTOMER (CUST_NO, CUSTOMER, CITY, COUNTRY) VALUES ('2008', 'Asinine Vacation Rentals', 'Lihue', 'USA'); INSERT INTO CUSTOMER (CUST_NO, CUSTOMER, CITY, COUNTRY) VALUES ('2009', 'Fax', 'Turtle Island', 'Fiji'); INSERT INTO CUSTOMER (CUST_NO, CUSTOMER, CITY, COUNTRY) VALUES ('2010', 'FPC Corporation', 'Tokyo', 'Japan'); INSERT INTO CUSTOMER (CUST_NO, CUSTOMER, CITY, COUNTRY) VALUES ('2011', 'Dynamic Intelligence Corp', 'Zurich', 'Switzerland'); INSERT INTO CUSTOMER (CUST_NO, CUSTOMER, CITY, COUNTRY) VALUES ('2012', '3D-Pad Corp.', 'Paris', 'France'); INSERT INTO CUSTOMER (CUST_NO, CUSTOMER, CITY, COUNTRY) VALUES ('2013', 'Swen Export, Ltd.', 'Milan', 'Italy'); INSERT INTO CUSTOMER (CUST_NO, CUSTOMER, CITY, COUNTRY) VALUES ('2014', 'Graeme Consulting', 'Brussels', 'Belgium'); INSERT INTO CUSTOMER (CUST_NO, CUSTOMER, CITY, COUNTRY) VALUES ('2015', 'Klenin Inc.', 'Den Haag', 'Netherlands'); </sql>

Ein Beispiel als Grundlage

Projekt und Komponenten

Erzeugen Sie zuerst ein neues Projekt in Lazarus.

Für den Zugriff auf die Datenbank benötigen wir je eine TIBConnection, TSQLTransaction und TSQLQuery von der Seite 'SQLdb' in der Komponentenpalette. Die erste Komponente ist spezifisch für den Zugriff auf Firebird-Datenbanken.

Die beiden anderen Komponenten kommen bei allen Datenbanken zum Einsatz, auf die Sie mit SQLdb zugreifen können. Für die Anzeige der Daten verwenden wir eine TDBGrid Komponente, die Sie auf der Seite 'Data Controls' finden. Für die Verbindung mit den Datenbankkomponenten benötigen wir noch eine TDatasource Komponente von der Seite 'Data Access'. Zum Auslösen der Aktionen verwenden wir einen TButton von der Seite 'Standard'.

Damit haben wir alle Komponenten, die wir für das erste Beispiel benötigen. Vergrößern Sie das TDBGrid noch etwas, damit genügend Platz für die Anzeige der Daten zur Verfügung steht.

Die Komponenten verknüpfen

Als nächstes müssen wir die Komponenten miteinander verbinden. Am einfachsten geht das im Objektinspektor. Man kann die Zuweisungen aber auch im Quelltext vornehmen. Ändern Sie die 'Transaction' Eigenschaft von IBConnection1 auf 'SQLTransaction1'. Damit wird die 'Database' Eigenschaft von SQLTransaction1 automatisch auf 'IBConnection1' eingestellt. Als nächstes setzen Sie die 'Database' Eigenschaft von SQLQuery1 auf 'IBConnection1'. Lazarus ergänzt dann automatisch die 'Transaction' Eigenschaft. Die 'Dataset' Eigenschaft von Datasource1 ändern Sie auf 'SQLQuery1'. Zum Schluss müssen Sie noch die 'Datasource' Eigenschaft von DBGrid1 auf 'Datasource1' ändern.

Mit der Datenbank verbinden

Wie kommen jetzt die Daten aus der Datenbank auf den Bildschirm? Zunächst müssen wir IBConnection1 mitteilen, wo sich die employee.fdb befindet (üblicherweise im .../examples/empbuild/ Unterverzeichnis Ihrer Firebird-Installation). Sie haben jetzt wieder die Wahl, ob Sie auf den Objektinspektor zurückgreifen wollen, oder das Verzeichnis im Quelltext zuweisen wollen.

Wir entscheiden uns hier für den Objektinspektor. Ändern Sie die 'DatabaseName' Eigenschaft von IBConnection1 auf den Pfad zur Datei employee.fdb (z.B. C:\Programme\Firebird\Firebird_2_0\examples\empbuild\EMPLOYEE.FDB).

Wenn Sie sich den Pfad nicht merken können, dann können Sie ihn in der Regel auch aus Ihrem Dateimanager mittels 'kopieren und einfügen' eintragen. Bevor der Datenbank-Server Daten herausrückt, wird er die Berechtigung über Benutzername und Passwort abfragen. Eine ernsthafte Datenbank-Anwendung wird daher beim Programmstart den Benutzer danach fragen, um die Daten im geeigneten Moment an den Datenbank-Server weiterzureichen.

Wir werden der Einfachheit halber wieder auf den Objektinspektor zurückgreifen. Ändern Sie die 'UserName' Eigenschaft auf 'SYSDBA' und 'Password' auf 'masterkey'.

Um zu prüfen, ob die bisher vorgenommenen Einstellungen formal korrekt sind, können Sie die 'Connected' Eigenschaft auf 'True' setzen. Ist der Pfad nicht richtig oder stimmt der Benutzername oder das Passwort nicht, dann erscheint eine Fehlermeldung. War die Verbindung erfolgreich, dann trennen Sie diese jetzt wieder (auf 'False' setzen).

Sie sollten jetzt etwas ähnliches haben wie im folgenden Screenshot:

Einrichtung der Form und Komponenten

Auswählen, welche Daten angezeigt werden sollen

Trotz der erfolgreichen Verbindung wurden aber immer noch keine Daten angezeigt. Das erklärt sich dadurch, dass 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 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).

Der DataDesktop in Aktion

Zurück zu unserem Beispiel. Wir wollen alle Daten aus der Tabelle 'CUSTOMER' anzeigen. Der SQL Befehl dazu lautet:

select * from CUSTOMER

Diesen Befehl müssen wir der 'SQL' Eigenschaft von SQLQuery1 zuweisen. Im Quelltext unseres Programms würde das so aussehen:

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

Wird der Befehl in Textform angegeben, dann muss er in einfache Anführungszeichen eingeschlossen werden. Man könnte natürlich auch den Inhalt einer anderen Komponente zuweisen.

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:

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

end; </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: <pascal> SQLQuery1.Close; </pascal> Da wir nicht sicher sein können, ob SQLQuery1 noch irgendwelche Daten enthält, rufen wir die 'Clear' Prozedur auf: <pascal> SQLQuery1.SQl.Clear; </pascal> Dann weisen wir der 'SQL' Eigenschaft unsere SQL Anweisung zu: <pascal> SQLQuery1.SQL.Text := 'select * from CUSTOMER'; </pascal> Jetzt müssen wir die Verbindung zur Datenbank herstellen sowie die Transaktion aktivieren und dann die Abfrage öffnen: <pascal> IBConnection1.Connected := True; SQLTransaction1.Active := True; SQLQuery1.Open; </pascal> 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, dass 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):

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

end; </pascal>

Beim Trennen verwenden wir die umgekehrte Reihenfolge wie beim Verbinden: <pascal> SQLQuery1.Close; SQLTransaction1.Active := False; IBConnection1.Connected := False; </pascal>

Zusammenfassung

Wir haben bisher gelernt, wie man mit dem SQLdb Package eine Verbindung zu einer Firebird Datenbank herstellt und die Daten einer Tabelle am Bildschirm anzeigt.

Erweiterung von Beispiel 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üsste 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, dass 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, dass Datenbankfehler abgefangen werden können und der Anwender nicht im Regen stehen gelassen 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.

Siehe auch