Verwenden von SQLite mit FireDAC

Aus RAD Studio
Wechseln zu: Navigation, Suche

Nach oben zu Arbeiten mit DBMS (FireDAC)


Dieser Referenzartikel ist in mehrere Abschnitte gegliedert:

  • Einführung in SQLite: bietet einen Überblick über SQLite-Funktionen, fehlende Funktionen, mögliche Anwendungen und nicht für SQLite geeignete Anwendungen.
  • Verwenden von SQLite-Datenbanken: erläutert, wie eine SQLite-Datenbank in einer Delphi-Anwendung erstellt, eine Verbindung mit der Datenbank hergestellt und die Datenbank verwaltet wird.
  • Verschlüsselte SQLite-Datenbanken: die Datenbankverschlüsselung stellt eine der wichtigsten SQLite-Funktionen dar. In diesem Abschnitt wird die Funktionsweise der Verschlüsselung und deren Steuerung erklärt.
  • SQLite-Datentypen: SQLite verwendet ein spezielles Datentypsystem. Ohne Kenntnis der Funktionsweise dieses Systems ist es schwierig, Daten in Delphi-Anwendungen zu speichern und abzurufen.
  • SQL-Anweisungen von SQLite: Hauptaspekte des SQL-Dialekts von SQLite für Delphi-Anwendungsentwickler.
  • Transaktionen, Sperren und Cursors in SQLite: erläutert die Arbeit mit Transaktionen in einer SQLite-Umgebung.
  • Erweitern der SQLite-Engine: als eingebettetes DBMS kann die SQLite-Engine durch Delphi-Anwendungscode erweitert werden.
  • Fortgeschrittene SQLite-Techniken: in diesem Abschnitt werden schließlich einige fortgeschrittene SQLite-Konzepte, wie Protokollierung von Aktualisierungen und SQL-Autorisierung, vorgestellt.

Für diesen Artikel ist die Kenntnis der FireDAC-Grundlagen und der Hauptbibliotheks-APIs erforderlich. Anfängern wird empfohlen, mit dem Artikel Einführung zu beginnen und sich die Demo "FireDAC\Samples\Getting Started\SQLite" anzusehen.

Inhaltsverzeichnis

Einführung in SQLite

SQLite-Datenbank

SQLite (EN) ist eine eingebettete SQL-Datenbank-Engine, die vom SQLite Consortium entwickelt wurde. Sie ist das weltweit am häufigsten eingesetzte DBMS (EN) mit circa 500 Millionen Installationen. Sie wird auf mobilen iOS- und Android-Geräten genauso wie auf Mac OS-Desktops eingesetzt. Sie wird von Firefox, Skype und McAfee-Antivirus verwendet.

SQLite-Funktionen

Auf der SQLite-Website (EN) sind die folgenden Funktionen aufgeführt:

  • Transaktionen sind ACID (Atomic, Consistent, Isolated und Durable), auch nach Systemabstürzen und Stromausfällen.
  • Konfigurationsfrei – keine Konfiguration oder Administration erforderlich.
  • Implementiert einen Großteil der SQL92-Funktionen. Unterstützt Tabellen-Trigger und -Sichten.
  • Eine komplette Datenbank wird in einer einzelnen plattformübergreifenden Datei gespeichert.
  • Unterstützt Datenbanken in Terabyte-Größe sowie Strings und Blobs in Gigabyte-Größe.
  • Schneller als populäre Client/Server-Datenbank-Engines bei den häufigsten Operationen.
  • Eigenständig: keine externen Abhängigkeiten.
  • Geräteübergreifend: Windows, macOS, iOS und Android werden ohne Zusatzaufwand unterstützt.
  • Die Quelltexte befinden sich in der öffentlichen Domäne. Deren Verwendung für beliebige Zwecke ist kostenlos.
  • Sehr leistungsstarke API, die die Erweiterung der Engine in praktisch allen Bereichen zulässt.
  • SQLite erreicht im Vergleich zu anderen eingebetteten, Dateiserver- und Client-Server-Datenbank-Engines, die in Delphi-Anwendungen eingesetzt werden, eine der besten Datenzugriffsleistungen. Es gibt viele erfolgreiche Datenbankenanwendungen mit mehreren Gigabyte. Silwood Technology Saphir wurde beispielsweise mit SQLite, Delphi und FireDAC entwickelt.

Fehlende SQLite-Funktionen

Entwickler suchen häufig nach den folgenden Funktionen, die in SQLite nicht vorhanden sind:

  • Gespeicherte Prozeduren – FireDAC bietet eine API für benutzerdefinierte Funktionen.
  • Umfangreiche integrierte Funktionen – FireDAC installiert ungefähr 50 Standardfunktionen vor.
  • Sicherheitssystem, einschließlich Benutzerkonzept und Rechtekonzept – FireDAC bietet eine passwortgeschützte verschlüsselte Datenbank und spezielle Callback-Funktionen zum Filtern von Benutzeraktionen.
  • Sortierungen (nur ASCII und binär) – FireDAC ermöglicht die Definition von benutzerdefinierten Sortierungen.

SQLite-Anwendungen

Auf der SQLite-Website (EN) sind die folgenden Funktionen aufgeführt:

  • Format der Anwendungsdatei – SQLite wurde mit großem Erfolg als On-Disk-Dateiformat für Desktop-Plattformen eingesetzt.
  • Eingebettete Geräte und Anwendungen – Weil die SQLite-Datenbank keine oder nur wenig Administration erfordert, ist SQLite eine gute Wahl für Geräte oder Dienste, die unbeaufsichtigt und ohne menschliche Unterstützung arbeiten.
  • Websites – SQLite funktioniert in der Regel als Datenbank-Engine für Websites mit geringem oder mittlerem Verkehr (d. h. 99,9 % aller Websites) gut.
  • Ersatz für Ad-hoc-Datenträgerdateien – SQLite funktioniert insbesondere gut als Ersatz für diese Ad-hoc-Datendateien.
  • Interne oder temporäre Datenbanken – In Programmen kann das Sichten und Sortieren von Daten auf unterschiedliche Weise einfacher ausgeführt werden, indem Daten in die SQLite-Datenbank im Arbeitsspeicher geladen werden und die komplette SQL-Anweisung ausgeführt wird.
  • Ersatz für eine Unternehmensdatenbank bei Demos oder Tests.
  • Datenbankpädagogik.

Nicht für SQLite geeignete Anwendungen

Auf derselben SQLite-Website (EN) sind die folgenden Funktionen aufgeführt:

  • Hohe Parallelität – SQLite verwendet Lese-/Schreibsperren für die gesamte Datenbankdatei. Wenn also ein Prozess einen Teil der Datenbank liest, können alle anderen Prozesse nicht in einen anderen Teil der Datenbank schreiben. Ebenso können alle anderen Prozesse keinen anderen Teil der Datenbank lesen, während ein Prozess in die Datenbank schreibt.
  • Client/Server-Anwendungen – Wenn viele Client-Programme auf eine gemeinsame Datenbank über ein Netzwerk zugreifen, sollten Sie die Verwendung einer Client/Server-Datenbank-Engine anstelle von SQLite in Betracht ziehen. SQLite arbeitet über ein Netzwerkdateisystem, aber wegen der den meisten Netzwerkdateisystemen zugeordneten Wartezeit ist die Leistung nicht ideal.
  • Sehr große Datenmengen (N TB).

Verwenden von SQLite-Datenbanken

Verbinden mit einer SQLite-Datenbank aus einer Delphi-Anwendung

Fügen Sie TFDPhysSQLiteDriverLink einem Formular oder einen Datenmodul hinzu, um den SQLite-Treiber von FireDAC mit einer Delphi-Anwendung zu verknüpfen. Um eine Verbindung mit einer SQLite-Datenbank herzustellen, geben Sie SQLite-Treiberparameter an, zumindest die folgenden:

DriverID=SQLite
Database=<path to SQLite database>

Standardmäßig werden alle SQLite-Treibereinstellungen für einen einzelnen Hochleistungszugriff auf eine Datenbank in einer stabilen Umgebung festgelegt. Mit der Anweisung PRAGMA (EN) können Sie SQLite konfigurieren. Viele FireDAC SQLite-Treiberparameter entsprechen Pragma-Anweisungen. Darüber hinaus ermöglicht SQLiteAdvanced die Angabe von mehreren durch ";" getrennten Pragma-Anweisungen als einzelnen Verbindungsparameter.

Weitere SQLite-Anwendungsfälle:

Nr. Besonderheiten der Anwendung Beschreibung
1 Lesen großer DBs. Setzen Sie CacheSize auf eine höhere Anzahl an Seiten, die für die Zwischenspeicherung der DB-Daten verwendet werden. Die gesamte Größe des Zwischenspeichers beträgt "CacheSize * <DB-Seitengröße>".
2 Exklusives Aktualisieren von DBs. Setzen Sie JournalMode ggf. auf WAL (EN) (weitere Informationen (EN)).
3 Lange Aktualisierungstransaktionen. Setzen Sie CacheSize auf eine höhere Anzahl an Seiten. Dadurch können Sie Transaktionen mit vielen Aktualisierungen ausführen, ohne den Arbeitsspeicher-Zwischenspeicher mit modifizierten Seiten zu überladen.
4 Einige wenige gleichzeitige Aktualisierungsprozesse. Setzen Sie LockingMode auf Normal, um den gemeinsamen DB-Zugriff zu aktivieren. Setzen Sie Synchronous auf Normal oder Full, damit übergebene Daten für andere sichtbar sind. Setzen Sie UpdateOptions.LockWait auf True, um das Warten auf Sperren zu aktivieren. Erhöhen Sie BusyTimeout, um die Wartezeit auf Sperren zu verlängern. Setzen Sie JournalMode ggf. auf WAL.
5 Einige wenige gleichzeitige Aktualisierungs-Threads. Siehe (4). Setzen Sie zudem SharedCache auf False, um Sperrkonflikte zu minimieren.
6 Einige wenige gleichzeitige Aktualisierungstransaktionen. Siehe (4) oder (5). Setzen Sie zudem TxOptions.Isolation auf xiSnapshot oder xiSerializible, um mögliche Transaktions-Deadlocks zu verhindern.
7 Hohe Sicherheit. Setzen Sie Synchronous auf Full, um die DB vor Verlusten von übergebenen Daten zu schützen.Siehe auch(3). Ziehen Sie die Verschlüsselung der Datenbank in Betracht, um die Integrität zu wahren.
8 Hohe Vertraulichkeit. Verschlüsseln Sie die Datenbank, um Vertraulichkeit und Integrität bereitzustellen.
9 Entwicklungszeit. Setzen Sie LockingMode auf Normal, um die simultane Verwendung der SQLite-DB in der IDE und einem gedebuggten Programm zu aktivieren.

Erstellen einer SQLite-Datenbank in einer Delphi-Anwendung

Standardmäßig wird die SQLite-Datenbank bei der Verbindungseinrichtung erstellt, falls sie nicht vorhanden ist. Um eine explizite Steuerung zu erreichen, kann in der Delphi-Anwendung Folgendes angegeben werden:

OpenMode=CreateUTF8 | CreateUTF16 | ReadWrite | ReadOnly

Die beiden ersten für die Erstellung verwendeten Werte unterscheiden sich hinsichtlich der Codierung, die für die neue Datenbank verwendet wird. Außerdem wird empfohlen, page_size (EN) für Datenbanken, die Tabellen mit mehreren Zeilen enthalten, auf 4096 oder höher zu setzen. Dafür können Sie bei der Erstellung den folgenden Code angeben:

SQLiteAdvanced=page_size=4096

Ziehen Sie die Angabe von Parametern mittels SQLiteAdvanced in Betracht:

Nach Erstellung der Datenbankdatei hat sie eine Größe von null. Als Folge werden Datenbankcodierung, Seitengröße und andere persistente Parameter nicht in der Datenbank aufgezeichnet. Damit diese Parameter persistent werden, muss in der Anwendung mindestens eine Tabelle erstellt werden.

Verwenden einer SQLite-Datenbank im Arbeitsspeicher in einer Delphi-Anwendung

Die nächste SQLite-spezifische Funktion ist die Möglichkeit, mit reinen Arbeitsspeicherdatenbanken (EN) zu arbeiten. Das bedeutet, dass keine Daten zum Speichern von Datenbankobjekten erstellt werden, sondern alles im Arbeitsspeicher verbleibt. Auf diese Weise profitieren Sie in einer Delphi-Anwendung von einer höheren Sicherheit, einer größeren Leistung und weniger Anforderungen für Zugriffsrechte in der Umgebung.

Verwenden Sie die folgenden Parameter, um eine SQLite-Datenbank im Arbeitsspeicher zu erstellen und zu öffnen:

DriverID=SQLite
Database=:memory:

Oder lassen Sie den Parameter Database einfach leer:

DriverID=SQLite

Ein FireDAC-Kunde hat eine SQLite-Datenbank auf einer freigegebenen Netzwerkressource gespeichert. Die Datenbank ist ein schreibgeschützter Produktkatalog mit vielen gespeicherten Produktattributen. Um die Leistung extrem zu steigern, hat der Kunde mit TFDSQLiteBackup die gesamte Datenbank in die Datenbank im Arbeitsspeicher verschoben. Der Beispielcode:

FDConnection1.DriverName := 'SQLite';
FDConnection1.Open;

FDSQLiteBackup1.Database := '\\srv\db\data.sdb';
FDSQLiteBackup1.DestDatabaseObj := FDConnection1.CliObj;
FDSQLiteBackup1.DestMode := smCreate;
FDSQLiteBackup1.Backup;

Arbeiten mit Unicode und SQLite-Datenbanken

FireDAC unterstützt Unicode von Delphi (ab Delphi 2009) vollständig. Für SQLite bedeutet das:

  • FireDAC richtet automatisch eine SQLite-Datenbank für den Austausch aller Metadaten in der UTF-16-Codierung ein, wenn Delphi 2009 oder höher verwendet wird. In Delphi 2007 oder früher sind die Metadaten in ANSI codiert.
  • Die Daten werden so definiert und ausgetauscht, wie im Kapitel "Zuordnen von SQLite- zu FireDAC-Datentypen" beschrieben.

Verwenden von mehreren SQLite-Datenbanken in einer Delphi-Anwendung

SQLite ermöglicht die Verwendung mehrerer Datenbanken in einer einzelnen Verbindung. Eine im Parameter Database angegebene DB ist die Hauptdatenbank. Um weitere Datenbanken hinzuzufügen, muss in der Delphi-Anwendung die ATTACH-Anweisung (EN) verwendet werden. Zum Beispiel:

FDConnection1.ExecSQL('ATTACH ''c:\hr.sdb'' AS hr');
FDConnection1.ExecSQL('ATTACH ''c:\cust.sdb'' AS cust');
FDQuery1.Open('select * from "Orders" o ' +
  'left join hr."Employees" e on o.EmployeeID = e.EmployeeID ' +
  'left join cust."Customers" c on o.CustomerID = c.CustomerID');
Hinweis: FireDAC interpretiert einen Datenbanknamen als Katalognamen.

Verwalten einer SQLite-Datenbank in einer Delphi-Anwendung

In einer guten Delphi-SQLite-Datenbankanwendung (und nicht nur dort) muss Folgendes berücksichtigt werden:

  • Die SQLite-Datenbank kann nach vielen "harten" Datensatzaktualisierungen oder -löschungen fragmentiert und nicht optimal sein. Durch Aufruf der Methode TFDSQLiteValidate.Sweep wird die Datenbank optimiert. Diese Methode entspricht den Anweisungen VACUUM (EN) und PRAGMA auto_vacuum (EN). Beispiel:
FDSQLiteValidate1.Database := 'c:\db.sdb';
FDSQLiteValidate1.Sweep;
  • Durch die Optimierung von SQLite-Abfragen wird ein besserer Abfrageausführungsplan erstellt, wenn eine aktuelle Datenbankstatistik vorliegt. SQLite aktualisiert die Statistik nicht automatisch. Durch Aufruf der Methode TFDSQLiteValidate.Analyze wird die Statistik erfasst. Diese Methode verwendet die Anweisung ANALYZE (EN). Eine Anwendung kann die Statistik für die vollständige Datenbank erfassen:
FDSQLiteValidate1.Database := 'c:\db.sdb';
FDSQLiteValidate1.Analyze;
  • Die SQLite-Datenbank kann beschädigt oder nicht wohlgeformt werden. Verwenden Sie die Methode TFDSQLiteValidate.CheckOnly, um die Integrität zu überprüfen. Um eine beschädigte SQLite-Datenbank zu reparieren, muss die Delphi-Anwendung sie aus einer Sicherung wiederherstellen. Die Methode CheckOnly verwendet die Ereignisbehandlungsroutine OnProgress, um über Probleme zu informieren. Diese Methode führt die Anweisung PRAGMA integrity_check (EN) aus.
procedure TForm1.FDSQLiteValidate1Progress(ASender: TFDPhysDriverService; const AMessage: String);
begin
  Memo1.Lines.Add(AMessage);
end;

FDSQLiteValidate1.Database := 'c:\db.sdb';
FDSQLiteValidate1.OnProgress := Form1Progress;
FDSQLiteValidate1.CheckOnly;
  • Die SQLite-Datenbank muss regelmäßig gesichert werden, um Datenverluste zu vermeiden. Die Komponente TFDSQLiteBackup führt eine Sicherung der Datenbank durch. Der einfachste Code zum Ausführen einer Sicherung lautet wie folgt:
FDSQLiteBackup1.Database := 'c:\db.sdb';
FDSQLiteBackup1.DestDatabase := 'c:\db.backup';
FDSQLiteBackup1.DestMode := smCreate;
FDSQLiteBackup1.Backup;

Verschlüsselte SQLite-Datenbanken

Ansatz

Eine der spezifischen SQLite-Funktionen ist extrem schnelle, starke Verschlüsselung von Datenbanken. Damit wird der Inhalt der Datenbankdatei vertraulich und die Integritätssteuerung für die Datenbankdatei verstärkt.

Das verschlüsselte Datenbankformat ist nicht mit anderen ähnlichen SQLite-Verschlüsselungserweiterungen kompatibel. Das bedeutet, dass Sie keine verschlüsselte Datenbank verwenden können, die mit Nicht-FireDAC-Bibliotheken verschlüsselt wurden. Wenn Sie eine derartig verschlüsselte Datenbank verwenden möchten, dann müssen Sie die Datenbank mit dem Originaltool entschlüsseln und mit FireDAC erneut verschlüsseln.

Die Verschlüsselung wird im offiziell unterstützten SQLite-Ansatz bereitgestellt – benutzerdefinierter Codec-Code und Compilierung mit definiertem SQLITE_HAS_CODEC. Alle Verschlüsselungsroutinen sind in Delphi implementiert und im sqlite3-Code eingebettet. Daher wird die Verschlüsselung für folgende Elemente korrekt behandelt:

Verschlüsselungsmodi

Modus Beschreibung Verwendung
AES-NNN Die Algorithmen aes-NNN sind generische Zusammensetzungen von AES-CTR und AES-CBC-MAC. Diese Zusammensetzungen garantieren Vertraulichkeit und Integrität. Das bedeutet, dass nur Entitäten mit Zugriff auf das korrekte Passwort die Seiten der verschlüsselten Datenbank lesen und ändern können. Diese Algorithmen fügen der verschlüsselten Datenbank einen linearen Overhead von 32 Byte pro Seite hinzu.
Mit diesem Algorithmus können Sie die bösartigsten Versuche aufdecken, Daten in die Datenbank einzufügen, aber solche Versuche werden nicht verhindert, und Sie erhalten keine Unterstützung für die Rückgängigmachung derartiger Änderungen. Es handelt sich um Grunde um eine Ergänzung häufiger Sicherungen, aber dieser Algorithmus ist besser als die meisten anderen Verschlüsselungsschemas für Datenbanken, da er eine Warnung ausgibt, wenn Sie Ziel eines Angriffs geworden sind und Sie Ihre Datenbank aus einer Sicherung wiederherstellen sollten. Bitte beachten Sie, das der aes-NNN-Algorithmus alleine weder die Löschung von ganzen Seiten am Ende der Datenbank (sondern nur Löschungen in der Mitte der Datenbank), noch Angriffe entdeckt, die darin bestehen, die Datenbank auf eine ältere Version zurückzusetzen, die mit demselben Passwort verschlüsselt ist.
Der AES-NNN-Algorithmus stellt eine optimale Vertraulichkeit und Integrität bereit. Daraus resultiert jedoch eine gewisse Leistungsminderung, die gegenüber anderen Verschlüsselungsmodi deutlich wahrnehmbar ist.
AES-CTR-NNN Die Algorithmen aes-ctr-NNN umfassen nur AES-CTR. Sie erkennen keine Änderungen an der Datenbank, aber Sie stellen Vertraulichkeit bei passiven Angriffen bereit. Solange ein Angreifer keinen Zugriff au Ihr Passwort hat und nicht versucht, die Datenbank zu ändern, um auszuprobieren, wie Ihre Anwendung auf die Änderungen reagiert, bleiben Ihre Daten so geheim, wie Ihre Anwendung es zulässt.

Die Algorithmen schützen die Datenbankdatei nur gegen Angreifer, die nicht in der Lage sind, in Ihre FireDAC-Anwendung einzudringen, beispielsweise mit einem Debugger, und das Passwort so zu extrahieren. Wenn Sie Ihr Passwort in einer Konfigurationsdatei oder als eine Konstante in der Software selbst speichern, ist es sogar für nicht sehr versierte Angreifer ein Kinderspiel es zu finden und Ihre Sicherheit zu gefährden.

Der AES-CTR-NNN-Algorithmus stellt eine optimale Vertraulichkeit, aber keine Integrität bereit. Damit erzielen Sie eine bessere Leistung.
AEC-ECB-NNN Die Algorithmen aes-ecb-NNN umfassen nur AES-ECB. Sie erkennen keine Änderungen an der Datenbank und stellen keine Vertraulichkeit bei passiven Angriffen bereit, im Gegensatz zu AES-NNN und AES-CTR-NNN. Der Algorithmus AES-ECB-NNN stellt eine vergleichsweise schwache Vertraulichkeit und keine Integrität bereit. Er liefert jedoch im Vergleich mit anderen Verschlüsselungsmodi die beste Leistung.

NNN ist die Schlüsselgröße; 128, 192 oder 256 Bit.

Konfigurieren der Verschlüsselung

Die Verschlüsselung kann durch Folgendes gesteuert werden:

  • durch die Verbindungsdefinitionsparameter Encrypt, NewPassword und Password.
  • durch die Dienstkomponente TFDSQLiteSecurity.

Die password-Verbindungsdefinitionsparameter können die folgende Form haben:

[aes-128 | aes-192 | aes-256 | aes-ctr-128 | aes-ctr-192 | aes-ctr-256 |
 aes-ecb-128 | aes-ecb-192 | aes-ecb-256:] password

"aes-XXX-NNN": ist ein optionaler Präfix, der den zu verwendenden Verschlüsselungsalgorithmus steuert. Wenn er nicht angegeben ist, dann wird Folgendes verwendet:

  • ein Algorithmus, der im Parameter Encrypt angegeben ist
  • aes-256, wenn nichts angegeben ist

FireDAC unterstützt Verschlüsselungsoperationen:

Operation Verwendung von Parametern Verwendung von TFDSQLiteSecurity
Öffnen einer verschlüsselten Datenbank Password=xxxx ---
Verschlüsseln einer nicht verschlüsselten Datenbank NewPassword=xxxx FDSQLiteSecurity1.Database := '...';

FDSQLiteSecurity1.Password := 'xxxx';

FDSQLiteSecurity1.SetPassword;

Ändern des Passworts einer verschlüsselten Datenbank Password=xxxx

NewPassword=yyyy

FDSQLiteSecurity1.Database := '...';

FDSQLiteSecurity1.Password := 'xxxx';

FDSQLiteSecurity1.ToPassword := 'yyyy';

FDSQLiteSecurity1.ChangePassword;

Entschlüsseln einer verschlüsselten Datenbank Password=xxxx

NewPassword=

FDSQLiteSecurity1.Database := '...';

FDSQLiteSecurity1.Password := 'xxxx';

FDSQLiteSecurity1.RemovePassword;

Überprüfen des Verschlüsselungsstatus einer Datenbank ---

NewPassword=

FDSQLiteSecurity1.Database := '...';

FDSQLiteSecurity1.Password := 'xxxx';

ShowMessage(FDSQLiteSecurity1.CheckEncryption);

SQL-Erweiterung

Die Anweisung ATTACH hat eine Erweiterung. Die vollständige Syntax von ATTACH lautet nun:

ATTACH [DATABASE] 'filename' [AS name] [KEY 'password']

Wenn KEY weggelassen wird, dann wird der Passwortwert von der Hauptdatenbank geerbt. Um ein leeres Passwort zum Anfügen einer nicht verschlüsselten Datenbank anzugeben, verwenden Sie Code wie den folgenden:

ATTACH 'D:\tmp\test.db' AS tst KEY ''

SQLite-Datentypen

Zuordnen von SQLite- zu FireDAC-Datentypen

SQLite verwendet ein "typenloses" Datentypsystem (EN). Das bedeutet, dass Sie praktisch jeden beliebigen Bezeichner als Datentypname einer Spalte verwenden können. Beispielsweise funktioniert auch "Delphi" und entspricht dem String-Datentyp. Damit der SQLite-Ansatz kompatibler zu anderen DBMSs und Delphi, aber auch einfacher für Delphi-Anwendungsentwickler wird, erkennt FireDAC die in der folgenden Tabelle aufgeführten Datentypnamen:

Typname Beschreibung
ROWID | _ROWID_ | OID dtInt64, Attrs = [caSearchable, caAllowNull, caROWID]
BIT | BOOL | BOOLEAN | LOGICAL | YESNO dtBoolean
TINYINT | SHORTINT | INT8 [UNSIGNED] dtSByte / dtByte
BYTE | UINT8 dtByte
SMALLINT | INT16 [UNSIGNED] dtInt16 / dtUInt16
WORD | UINT16 | YEAR dtUInt16
MEDIUMINT | INTEGER | INT | INT32 [UNSIGNED] dtInt32 / dtUInt32
LONGWORD | UINT32 dtUInt32
BIGINT | INT64 | COUNTER | AUTOINCREMENT | IDENTITY [UNSIGNED] dtInt64 / dtUInt64
LONGLONGWORD | UINT64 dtUInt64
REAL | FLOAT | DOUBLE dtDouble
SINGLE [PRECISION] [(P, S)] dtSingle / dtBCD / dtFmtBCD
DECIMAL | DEC | NUMERIC | NUMBER [UNSIGNED] [(P, S)] dtSByte / dtInt16 / dtInt32 / dtInt64

dtByte / dtUInt16 / dtUInt32 / dtUInt64

dtBCD / dtFmtBCD

MONEY | SMALLMONEY | CURRENCY | FINANCIAL [(P, S)] dtCurrency
DATE | SMALLDATE dtDate
DATETIME | SMALLDATETIME dtDateTime
TIMESTAMP dtDateTimeStamp
TIME dtTime
CHAR | CHARACTER [(L)] dtAnsiString, Len = L, Attrs = [caFixedLen]
VARCHAR | VARCHAR2 | TYNITEXT | CHARACTER VARYING | CHAR VARYING [(L)] dtAnsiString, Len = L
NCHAR | NATIONAL CHAR | NATIONAL CHARACTER [(L)] dtWideString, Len = L, Attrs = [caFixedLen]
NVARCHAR | NVARCHAR2 | NATIONAL CHAR VARYING | STRING [(L)] dtWideString, Len = L
RAW | TYNIBLOB | VARBINARY | BINARY | BINARY VARYING [(L)] dtByteString, Len = L
BLOB | MEDIUMBLOB | IMAGE | LONGBLOB | LONG BINARY | LONG RAW | LONGVARBINARY | GENERAL | OLEOBJECT | TINYBLOB dtBlob
MEDIUMTEXT | LONGTEXT | CLOB | MEMO | NOTE | LONG | LONG TEXT | LONGCHAR | LONGVARCHAR | TINYTEXT dtMemo
TEXT | NTEXT | WTEXT | NCLOB | NMEMO | LONG NTEXT | LONG WTEXT | NATIONAL TEXT | LONGWCHAR | LONGWVARCHAR | HTML dtWideMemo
XMLDATA | XMLTYPE | XML dtXML
GUID | UNIQUEIDENTIFIER dtGUID
Andere Datentypen dtWideString
Hinweis: Mit SQLite funktioniert FormatOptions.StrsTrim für alle String-Datentypen.

Spezielle SQLite-Datentypen

Um einer Tabelle eine Auto-Inkrementierungsspalte (EN) hinzuzufügen, definieren Sie eine Spalte als INTEGER PRIMARY KEY AUTOINCREMENT. Dieser Typ wird zu dtInt32, Attrs = [caAutoInc] zugeordnet. Weitere Einzelheiten über die Behandlung von Auto-Inkrementierungsspalten finden Sie unter Auto-Inkrementierungsfelder.

Die Spalten mit den Typnamen ROWID, _ROWID_ oder OID werden als Kennzeichnungsspalten für Zeilen (EN) betrachtet. Diese Typen werden zu dtInt64, Attrs = [caSearchable, caAllowNull, caROWID] zugeordnet. Weitere Einzelheiten über die Behandlung von Kennzeichnungsspalten für Zeilen finden Sie unter Eindeutige Kennzeichnungsfelder. SQLite ROWID bietet den schnellsten Weg zum Zugreifen auf eine bestimmte Zeile:

SELECT * FROM Orders WHERE ROWID = :RID

Anpassen der FireDAC-Zuordnung

Bestimmte SQLite-Treiberparameter ermöglichen, dass in der Delphi-Anwendung die Datendarstellung geändert wird:

Parameter Beschreibung
StringFormat = Choose | Unicode Bei Unicode werden alle dtAnsiString- und dtMemo-Daten für einen Client als dtWideString- und dtWideMemo-Daten dargestellt.
GUIDFormat = String | Binary Bei Binary werden dtGUID-Daten in einer Datenbank als binäre TGUID-Werte gespeichert. Bei String werden die Daten als String im Format {xxxxxxx} dargestellt. Für Binary ist weniger Speicherplatz in der DB erforderlich, und String kann einfacher gelesen werden.
DateTimeFormat = String | Binary Bei Binary werden die dtDate-, dtTime-, dtDateTime-Daten in einer Datenbank als double-Wert im Julianischen Datumsformat gespeichert. Bei String werden die Daten als Zeichen-String im Format "jjjj-mm-tt hh24:mi:ss" gespeichert. Für Binary ist weniger Speicherplatz in der DB erforderlich, und String kann einfacher gelesen werden.
Hinweis: Wenn die Datenbank nicht leer ist und GUIDFormat oder DateTimeFormat geändert wird, können Fehler auftreten, weil FireDAC möglicherweise die gespeicherten Werte nicht lesen und analysieren kann.

Bei Ausdrücken in SELECT-Listen vermeidet SQLite Typnamensinformationen. Wenn die Ergebnismenge nicht leer ist, verwendet FireDAC die Wertdatentypen aus dem ersten Datensatz. Wenn die Ergebnismenge leer ist, beschreibt FireDAC diese Spalten als dtWideString. Um den Spaltendatentyp explizit anzugeben, hängen Sie "::<Typname>" an den Spaltenalias an:

SELECT count(*) as "cnt::INT" FROM mytab

Wenn für die Delphi-Anwendung die native SQLite-Datentyprepräsentation erforderlich ist, dann verwendet FireDAC Zuordnungsregeln. Zum Beispiel: Zuordnen von TEXT-Spalten zu dtAnsiString und von INT-Spalten zu dtInt64:

with FDQuery1.FormatOptions do begin
  OwnMapRules := True;
  with MapRules do begin
    SourceDataType := dtMemo;
    TargetDataType := dtAnsiString;
  end;
  with MapRules do begin
    SourceDataType := dtInt32;
    TargetDataType := dtInt64;
  end;
end;

Zahlen mit hoher Genauigkeit

Aufgrund des SQLite-Typsystems (EN) wird von langen Zahlen (>= 20 Stellen) angenommen, dass sie eine Affinität zum Typ REAL haben. Deshalb wird ein Wert auf einen REAL-Typ (double-Delphi-Typ) mit einer Genauigkeit von 15 Stellen gerundet. Um dieses Problem zu lösen, sollte in einer Anwendung TEXT oder ein ähnlicher Datentyp und eine Zuordnungsregel verwendet werden, mit der dtAnsiString in dtFmtBCD oder einen ähnlichen Datentyp umgewandelt wird. Hierbei handelt es sich nicht um ein FireDAC-Problem, und gegenwärtig kann es nicht anders gelöst werden.

SQL-Anweisungen in SQLite

SQL-Dialekt

SQLite ist nah an ANSI SQL 92 angelehnt, trotzdem werden einige Funktionen und Anweisungen nicht unterstützt, aber andere leistungsstarke wurden hinzugefügt. Weitere Informationen über den SQL.Dialekt von SQLite finden Sie unter:

SQLite-SQL-Anweisungsstapel

Der FireDAC SQLite-Treiber unterstützt die SQL-Anweisungsstapel. SQL-Anweisungen müssen durch ";" getrennt werden. In SQLite können Sie beliebige Anweisungen, einschließlich DDL und DML, in einem Stapel zusammen verwenden. Zum Beispiel:

with FDQuery1.SQL do begin

  SQL.Clear;
  SQL.Add('create table dbms (id integer, name varchar(20));');
  SQL.Add('insert into tab values (1, ''sqlite'');');
  SQL.Add('insert into tab values (2, ''mysql'');');
  SQL.Add('insert into tab values (3, ''firebird'');');
  SQL.Add('create table langs (id integer, name varchar(20));');
  SQL.Add('insert into tab values (1, ''delphi'');');
  SQL.Add('insert into tab values (2, ''c'');');
  SQL.Add('insert into tab values (3, ''c++'');');
  SQL.Add('select * from dbms;');
  SQL.Add('select * from langs;');

end;
FDQuery1.Open;
// process here the DBMS list
FDQuery1.NextRecordSet;
// process here the programming languages list

SQL-Skriptdialekt

TFDScript von FireDAC unterstützt keine Skriptsteuerungsanweisungen der SQLite-Syntax, die mit "." beginnen.

Array DML

Ab v 3.7.11 unterstützt SQLite die Anweisung INSERT (EN) mit mehreren VALUES. FireDAC implementiert mit diesem Feature Array DML, wenn "Params.BindMode = pbByNumber". Ansonsten emuliert FireDAC Array DML. Zum Beispiel:

// here ADQuery1.Params collection is filled by 3 parameters
FDQuery1.SQL.Text := 'insert into MyTab values (:p1, :p2, :p3)';
// set "by number" parameter binding mode
FDQuery1.Params.BindMode := pbByNumber;
FDQuery1.Params.ArraySize := 100;
for i := 0 to FDQuery1.Params.ArraySize - 1 do begin
  FDQuery1.Params[0].AsIntegers[i] := i;
  FDQuery1.Params[1].AsStrings[i] := 'qwe';
  FDQuery1.Params[2].Clear(i);
end;
FDQuery1.Execute(FDQuery1.Params.ArraySize);

Transaktionen, Sperren, Threads und Cursors in SQLite

Sperren und gleichzeitige Aktualisierungen

Nähere Informationen finden Sie in den Original-SQLite-Artikeln:

SQLite als Dateiserver-DBMS sperrt die Datenbanktabellen bei Aktualisierungen. Die folgenden Einstellungen wirken sich auf den gleichzeitigen Zugriff aus:

  • Setzen Sie den Verbindungsparameter SharedCache auf False, wenn mehrere Threads dieselbe Datenbank aktualisieren. Dadurch werden mögliche Deadlocks vermieden.
  • Setzen Sie LockingMode auf Normal, um den gleichzeitigen Zugriff auf die Tabellen zu ermöglichen, wenn mehrere Prozesse oder Threads dieselben Datenbanktabellen aktualisieren. Setzen Sie außerdem den Verbindungsparameter Synchronous auf Full oder Normal. Auf diese Weise aktualisiert SQLite eine Datenbankdatei direkt nach dem Abschluss der Transaktion und ermöglicht anderen Verbindungen die Kenntnisnahme über Aktualisierungen auf einer vorhersehbaren Basis.
  • Setzen Sie UpdateOptions.LockWait auf True und BusyTimeout auf einen höheren Wert, um Sperrkonfikte zwischen Verbindungen zu vermeiden.
  • Setzen Sie TFDConnection.TxOptions.Isolation auf xiSnapshot oder xiSerializible, um Sperrkonfikte zwischen länger laufenden Aktualisierungstransaktionen zu vermeiden.

Transaktionen und Isolationsmodi

SQLite unterstützt normale Transaktionen und verschachtelte Transaktionen (Prüfpunkte). Mehrere Transaktionen werden nicht unterstützt. Im Folgenden finden Sie eine Liste der von SQLite unterstützten Isolationsmodi:

Modus Entspricht
xiDirtyRead PRAGMA read_uncommitted (EN) = 1
xiReadCommitted BEGIN TRANSACTION DEFERRED (EN)
xiRepeatableRead Siehe xiReadCommitted.
xiSnapshot BEGIN TRANSACTION IMMEDIATE
xiSerializible BEGIN TRANSACTION EXCLUSIVE

Transaktionen und DML-Anweisungen

Durch das Einfügen von Schreib-Anweisungen in eine Transaktion kann die Leistung von SQLite erheblich gesteigert werden. Dies gilt besonders bei großen Datenänderungen. Dies kann auch für das FireDAC-Feature Array DML angewendet werden. Fügen Sie daher den Code für die Datenänderung in eine Transaktion ein, um die beste Leistung zu erzielen:

FDConnection1.StartTransaction;
try
  FDQuery1.SQL.Text := 'insert into tab values (:id, :name)';
  FDQuery1.Params.ArraySize := 10;
  for i := 0 to FDQuery1.Params.ArraySize - 1 do begin
    FDQuery1.Params[0].AsIntegers[i] := i;
    FDQuery1.Params[0].AsStrings[i] := 'name' + IntTostr(i);
  end;
  FDQuery1.Execute(FDQuery1.Params.ArraySize, 0);
  FDConnection1.Commit;
except
  FDConnection1.Rollback;
  raise;
end;

Transaktionen und Cursors

In SQLite ist das Zurücksetzen einer Transaktion nicht möglich, wenn Anweisungen mit Ergebnismengen vorhanden sind, die noch nicht abgerufen wurden. Um dies zu vermeiden, ruft FireDAC beim Aufruf der Methode Rollback alle verbleibenden Datensätze aus einer Ergebnismenge ab. Siehe FetchOptions.AutoFetchAll.

Erweitern der SQLite-Engine

Benutzerdefinierte Funktionen

SQLite unterstützt das Konzept der gespeicherten Prozeduren oder Funktionen nicht, da Sie die Funktionalität der Engine anhand der Host-Sprache erweitern können. SQLite ermöglicht die Registrierung der Host-Sprachfunktionen in der SQLite-Engine und deren Verwendung in SQL-Anweisungen. FireDAC vereinfacht dies durch Einführung der Komponente TFDSQLiteFunction.

Zum Erstellen einer Funktion muss der Entwickler FunctionName, ArgumentsCount setzen und die Ereignisbehandlungsroutine OnCalculate erstellen. Durch Setzen von Active auf True wird die benutzerdefinierte Funktion bei der SQLite-Engine registriert. Zum Beispiel:

procedure TForm1.FDSQLiteFunction1Calculate(AFunc: TSQLiteFunction;
  AInputs: TSQLiteInputs; AOutput: TSQLiteOutput; var AUserData: TObject);
begin
  AOutput.AsInteger := AInputs[0].AsInteger * AInputs[1].AsInteger;
end;

FDSQLiteFunction1.DriverLink := FDPhysSQLiteDriverLink1;
FDSQLiteFunction1.FunctionName := 'XmY';
FDSQLiteFunction1.ArgumentsCount := 2;
FDSQLiteFunction1.OnCalculate := FDSQLiteFunction1Calculate;
FDSQLiteFunction1.Active := True;

Die Verwendung dieser Funktion:

FDQuery1.Open('select RegionID, XmY(RegionID, 10) from "Region"');

Eine Funktion kann die FireDAC-Methoden zum Abfragen einer Datenbank aufrufen. Sie müssen denselben FunctionName mit der Standard- oder einer anderen Anzahl an Argumenten angeben, um eine benutzerdefinierte Funktion mit der Standard- oder einer anderen Anzahl an Argumenten zu erstellen. Damit wird in der SQLite-Engine eine überladene Funktion registriert.

Im Ordner "FireDAC\Samples\DBMS Specific\SQLite\UserFunc" finden Sie das obige und andere Funktionsbeispiele.

FireDAC implementiert und installiert für eine SQLite-Verbindung rund 50 Funktionen, die in der Praxis für viele DBMSs Standard sind und von der lokalen FireDAC-Ausdrucks-Engine implementiert werden. Beachten Sie bitte, dass Sie beim Einrichten einer SQLite-Verbindung zur Laufzeit die Unit FireDAC.Stan.ExprFuncs in die "uses"-Klausel aufnehmen müssen, sonst wird eine Exception ausgelöst:

[FireDAC][Phys][SQLite] ERROR: no such function: UCASE.

Erstellen Sie ein benutzerdefiniertes Entwurfszeit-Package mit einem Datenmodul, legen Sie die Komponenten in diesem Modul ab, und konfigurieren Sie es ordnungsgemäß, damit zur Entwurfszeit auf benutzerdefinierte Funktionen zugegriffen werden kann. Erstellen Sie das Modul im "initialization"-Abschnitt der Modul-Unit, und geben Sie es im "finalization"-Abschnitt frei. Installieren Sie danach Ihr Package in der Delphi-IDE.

Siehe auchdas Video von Ron Grove (EN).

Benutzerdefinierte Sortierungen

SQLite speichert und behandelt alle Zeichendaten abhängig vom Verbindungsparameter OpenMode entweder in UTF8 oder UTF16. Wenn SQLite Zeichendaten vergleichen oder sortieren muss, müssen die dafür zu verwendenden Regeln bekannt sein. Diese Regeln ergeben die Sortierung.

SQLite verfügt über mehrere integrierte Sortierungen. Keine davon erzeugt eine korrekte Sortierfolge für deutsche, kyrillische, arabische und andere Ausdrücke. Sie müssen die Komponente TFDSQLiteCollation zum Erstellen einer eigenen Sortierung verwenden. Legen Sie CollationName, Flags und LocaleName fest, und setzen Sie dann Active auf True, um die Sortierung bei der SQLite-Engine zu registrieren. Zum Beispiel:

FDSQLiteCollation1.DriverLink := FDPhysSQLiteDriverLink1;
FDSQLiteCollation1.CollationName := 'UTF16NoCase';
FDSQLiteCollation1.Flags := [sfIgnoreCase];
FDSQLiteCollation1.Active := True;

Die obige Komponentenkonfiguration mit dem Standardwert CollationKind=scCompareString implementiert eine Unicode-Standardsortierung, die Groß-/Kleinschreibung berücksichtigt. Die Anwendung kann Sortierungen mit CollationKind=scCustomUTF16 oder scCustomUTF8 und der Implementierung der Ereignisbehandlungsroutine OnCompare implementieren. Sie können diese Sortierung folgendermaßen verwenden:

SELECT * FROM "Employees" ORDER BY LastName COLLATE UTF16NoCase

Um eine Standardsortierung für eine Spalte festzulegen, können Sie folgenden Code verwenden:

CREATE TABLE IF NOT EXISTS test_col (f1 VARCHAR(10) COLLATE UTF16NoCase)
Hinweis: Wenn es keine Möglichkeit gibt, die Standardsortierung für eine Verbindung, eine Datenbank oder eine Tabelle festzulegen, finden Sie im Ordner "FireDAC\Samples\DBMS Specific\SQLite\UserCollation" die obigen Sortierbeispiele.

Wenn Sie keine eigenen Sortierungen verwenden, dann verwendet SQLite standardmäßig die binäre Sortierreihenfolge. Für den Modus Live-Datenfenster von TFDTable ist es wichtig, dass die Client-Seite und die Datenbank dieselbe Sortierreihenfolge verwendet. Setzen Sie FormatOptions.SortLocale auf 0, um die binäre Sortierreihenfolge auf der Client-Seite zu aktivieren.

Datenbankereignisse

FireDAC unterstützt Benachrichtigungen einer Delphi-Anwendung von einem SQLite-Datenbank-Trigger über bestimmte Ereignisse, z. B. eine Datenänderung. Dazu verwendet FireDAC ein ähnliches Vorgehen wie Firebird und registriert die benutzerdefinierte Funktion POST_EVENT. Gehen Sie folgendermaßen vor, um diese Funktion aus einen Trigger aufzurufen:

CREATE TRIGGER update_orders UPDATE ON "Orders"
BEGIN
  SELECT POST_EVENT('Orders');
END;

Zum Empfangen einer Ereignisbenachrichtigung verwendet die Delphi-Anwendung die Komponente TFDEventAlerter. Zum Beispiel:

FDEventAlerter1.Names.Text := 'Orders';
FDEventAlerter1.Options.Synchronize := True;
FDEventAlerter1.OnAlter := DoAlert;
FDEventAlerter1.Active := True;

procedure TForm1.DoAlert(ASender: TFDCustomEventAlerter;
  const AEventName: String; const AArgument: Variant);
begin
  if CompareText(AEventName, 'Orders') = 0 then
    qryOrders.Refresh;
end;

Benutzerdefinierte Datenquellen

Die Local SQL-Engine ermöglicht die Verwendung von TDataSet-Nachkommen in SQL-Abfragen. FireDAC implementiert Local SQL mittels der SQLite-API für virtuelle Tabellen (EN).

Fortgeschrittene SQLite-Techniken

Einbeziehen von Datenbankaktualisierungen

SQLite stellt eine spezielle API bereit, die die Überwachung aller Datenbankaktualisierungen (EN) ermöglicht. Damit können beispielsweise alle Aktualisierungen einer DB protokolliert werden. Zur Verwendung dieser API muss in einer Delphi-Anwendung die Ereignisbehandlungsroutine OnUpdate des Objekts TSQLiteDatabase, dem Objekt zum Kapseln der Datenbankverbindung, festgelegt werden. Beziehen Sie dieses Ereignis nach dem Öffnen der Datenbankverbindung ein. Zum Beispiel:

procedure TForm1.DoUpdate(ADB: TSQLiteDatabase; AOper: Integer; const ADatabase, ATable: String; ARowid: sqlite3_int64);
begin
  Memo1.Lines.Add(Format('%d - %s - %s - %u', [AOper, ADatabase, ATable, ARowid]));
end;

FDConnection1.Connected := True;
TSQLiteDatabase(FDConnection1.ConnectionIntf.CliObj).OnUpdate := DoUpdate;

Sie finden dieses Beispiel im Ordner "FireDAC\Samples\DBMS Specific\SQLite\OnUpdate".

Steuern von Datenbankzugriffsrechten

SQLite ist ein eingebettetes DBMS. Es arbeitet mit einem Einzelbenutzer-DBMS und benötigt keine Konzepte wie "Benutzer", "Zugriffsrechte" usw. Trotzdem kann eine Steuerung der Zugriffsrechte für manche Anwendungen sinnvoll sein, zum Beispiel:

  • Eine Anwendung kann Rechte abhängig von einer Endbenutzerlizenz einschränken. Eine Demolizenz beinhaltet weniger Möglichkeiten, eine Volllizenz bietet alle Möglichkeiten.
  • Mehrschichtige Datenzugriffs-Frameworks können eigene Konzepte verwenden und Datenzugriffsrechte durch ein generisches Vorgehen steuern.

Auch dafür bietet SQLite ein spezielles Feature, das die Autorisierung von SQL-Anweisungen zulässt oder nicht (EN). Zur Verwendung dieser API muss in einer Delphi-Anwendung die Ereignisbehandlungsroutine OnAutorize des Objekts TSQLiteDatabase, dem Objekt zum Kapseln der Datenbankverbindung, festgelegt werden. Beziehen Sie dieses Ereignis nach dem Öffnen der Datenbankverbindung ein. Zum Beispiel:

procedure TForm1.DoAuthorize(ADB: TSQLiteDatabase; ACode: Integer; const AArg1, AArg2, AArg3, AArg4: String; var AResult: Integer);
begin
  Memo1.Lines.Add(Format('%d - %s - %s - %s - %s', [ACode, AArg1, AArg2, AArg3, AArg4]));

  // Deny any delete operation
  if ACode = SQLITE_DELETE then
    AResult := SQLITE_DENY
  else
    AResult := SQLITE_OK;
end;

FDConnection1.Connected := True;
TSQLiteDatabase(FDConnection1.ConnectionIntf.CliObj).OnAutorize := DoAuthorize;

Sie finden dieses Beispiel im Ordner "FireDAC\Samples\DBMS Specific\SQLite\OnAuthorize".

Verwenden der Low-Level-API von SQLite

Wenn Sie die maximale Leistung für SQLite-Datenzugriffe erzielen möchten, dann sollten Sie die Verwendung der Kapselungsklassen der FireDAC SQLite-API in Betracht ziehen. Dabei handelt es sich um eine auf schlanke Low-Level-Objekte bezogene API, die vom FireDAC SQLite-Treiber verwendet wird. Diese API ist nicht dokumentiert und wird nicht offiziell unterstützt.

Das folgende Beispiel zeigt, wie Transaktionen gesteuert und Datensätze mit einer parametrisierten SELECT-Anweisung abgerufen werden:

uses
  FireDAC.Phys.SQLiteWrapper;

procedure TForm1.FormCreate(Sender: TObject);
var
  oDB: TSQLiteDatabase;
  oTran: TSQLiteStatement;
  oStmt: TSQLiteStatement;
  i: Integer;
begin
  FDConnection1.Connected := True;
  oDB := TSQLiteDatabase(FDConnection1.CliObj);

  oTran := TSQLiteStatement.Create(oDB);
  try
    // start transaction
    oTran.Prepare('BEGIN');
    oTran.Execute;

    oStmt := TSQLiteStatement.Create(oDB);
    try
      // prepare statement
      oStmt.Prepare('select * from "Orders" where OrderID > :ID1 and OrderID < :ID2');

      // add bind variables (parameters)
      for i := 1 to oStmt.ParamDefsCount do
        TSQLiteBind.Create(oStmt.Params);

      // add column variables (fields)
      for i := 1 to oStmt.ColumnDefsCount do
        TSQLiteColumn.Create(oStmt.Columns).Index := i - 1;

      // set parameter values and execute
      oStmt.Params[0].AsInteger := 11000;
      oStmt.Params[1].AsInteger := 12000;
      oStmt.Execute;

      // fetch records and read columns
      while oStmt.Fetch do
        Memo1.Lines.Add(Format('OrderID: %d, CustomerID: %s',
          [oStmt.Columns[0].AsInteger, oStmt.Columns[1].AsString]));
    finally
      oStmt.Free;
    end;

    // commit transaction
    oTran.Unprepare;
    oTran.Prepare('COMMIT');
    oTran.Execute;
  finally
    oTran.Free;
  end;
end;

Siehe auch

Beispiele