Ausführen von Anweisungen (FireDAC)

Aus RAD Studio
Wechseln zu: Navigation, Suche

Nach oben zu Arbeiten mit Anweisungen (FireDAC)

Verwenden von TFDConnection

TFDConnection stellt ExecSQL-Methoden bereit. Dabei handelt es sich um einfach zu verwendende überladene Methoden, die in folgenden Fällen hilfreich sind:

  • Es werden keine Ergebnismengen zurückgegeben.
  • Ein SQL-Anweisung wird nur einmal ausgeführt, daher muss sie nicht im vorbereiteten Status gespeichert werden.
  • Eine erweiterte Parameterkonfiguration ist nicht erforderlich.
  • Eine SQL-Anwendungskonfiguration zur Entwurfszeit ist nicht erforderlich.

Verwenden Sie beispielsweise die folgende Syntax, um DDL-Anweisungen auszuführen:

FDConnection1.ExecSQL('drop table testtab');
FDConnection1.ExecSQL('create table testtab (id integer, name varchar(10))');
FDConnection1.ExecSQL('insert into testtab values (1, ''FireDAC'')');

Verwenden Sie die folgende überladene Methode, um eine parametrisierte Abfrage auszuführen:

FDConnection1.ExecSQL('insert into testtab values (:id, :name)', [1, 'FireDAC']);

TFDConnection stellt auch ExecSQLScalar-Methoden bereit, die sich von ExecSQL-Methoden unterscheiden, da sie einen einzelnen Ergebnismengenwert zurückgeben:

sName := FDConnection1.ExecSQLScalar('select name from testtab where id = :id', [1]);


Verwenden von TFDQuery

Im Allgemeinen wird TFDQuery zur Entwurfszeit und/oder zur Laufzeit konfiguriert.


Konfigurieren von TFDQuery zur Entwurfszeit

Legen Sie eine TFDQuery-Komponente auf einem Formular ab, um sie zur Entwurfszeit zu konfigurieren. TFDQuery.Connection wird automatisch so festgelegt, dass auf eine TADConnection-Komponente in diesem Formular gezeigt wird, falls eine vorhanden ist. Doppelklicken Sie dann auf TFDQuery, um den Abfrage-Editor von FireDAC aufzurufen:

AD QueryDlg.png

Hier können Sie den SQL-Text und optional Parameter- und Makrowerte angeben und Optionen festlegen.

Drücken Sie die Schaltfläche Ausführen, um den Editor zu schließen. Wenn eine Anweisung eine Ergebnismenge zurückgibt, wird diese im Bereich RecordSet angezeigt, und die Struktur der Ergebnismenge wird im Bereich Struktur angezeigt. Wenn das DBMS Meldungen oder Warnungen für eine Anweisung zurückgibt, werden diese im Bereich Meldungen angezeigt. Mit der Schaltfläche Nächstes RecordSet können Sie durch alle von einer Anweisung zurückgegebenen Ergebnismengen blättern.

Wenn Sie DML-Anweisungen, wie UPDATE/INSERT/DELETE, testen, müssen Sie das Kontrollkästchen Automatisch zurücksetzen aktivieren, damit die von einer Anweisung durchgeführten Aktionen automatisch zurückgesetzt werden.

Drücken Sie OK, um die Änderungen in TFDQuery zu speichern.

Verwenden von Parametern

Die Verwendung von parametrisierten Abfragen ist eins der bewährtesten Verfahren bei der Entwicklung von SQL-Datenbankanwendung. Die Hauptvorteile sind:

  • Sie können eine einzelne SQL-Anweisung zusammenstellen und sie mehrmals mit verschiedenen Parameterwerten einsetzen. Das DBMS erstellt den Ausführungsplan für die Anweisung nur einmal. Dadurch wird die Auslastung des DBMS-Moduls reduziert.
  • Beim nächsten Ausführen der Anweisung werden nur die Parameterwerte an das DBMS-Modul übertragen. Dadurch wird die Auslastung des Netzwerks reduziert.
  • Sie brauchen sich nicht um korrekte SQL-Konstantenformate zu kümmern, z. B. darum, wie in Microsoft Access-SQL eine Datumskonstante richtig angegeben wird.

Verwenden Sie die Syntax :<Name>, um einen Parametermarker im SQL-Text zu setzen. Weisen Sie dann die entsprechenden Werte mit der Params-Sammlung zu. Zum Beispiel:

FDQuery1.SQL.Text := 'select * from tab where code = :Code';
FDQuery1.ParamByName('code').AsString := '123';
FDQuery1.Open;

Vor Ausführung der Abfrage müssen alle Parameter (Name, DataType und ParamType) ausgefüllt werden. Auch Position sollte festgelegt werden.

Wenn Sie die Eigenschaft SQL zuweisen und ResourceOptions.ParamCreate True ist, wird die Params-Sammlung automatisch gefüllt.

Hinweis: Nur TFDParam.Name und TFDParam.Position sind festgelegt. DataType, ParamType und andere Eigenschaften müssen in Ihrer Anwendung festgelegt werden. Die Parameter in der Params-Sammlung erscheinen in derselben Reihenfolge wie in der SQL-Anweisung.

Sie können die Params-Sammlung auch im Code füllen. Dazu müssen Sie die automatische Parametererstellung durch Setzen von ResourceOptions.ParamCreate auf False deaktivieren. Zum Beispiel:

FDQuery1.ResourceOptions.ParamCreate := False;
FDQuery1.SQL.Text := 'select * from tab where code = :Code';
with FDQuery1.Params do begin
  Clear;
  with Add do begin
    Name := 'CODE';
    DataType := ftString;
    Size := 10;
    ParamType := ptInput;
  end;
end;

Die Parameter können an die SQL-Anweisung entweder per Name oder per Position gebunden werden. Mit der Eigenschaft Params.BindMode können Sie dies steuern:

  • pbByName. Die Parameter in der Params-Sammlung werden durch die Eigenschaft Name an die entsprechenden Parametermarker gebunden. Wenn die SQL-Anweisung Parametermarker mit gleichen Namen hat, erscheint nur eine Instanz in Params.
  • pbByNumber. Die Parameter in der Params-Sammlung werden durch die Eigenschaft Position an die entsprechenden Marker im SQL-Anweisungstext gebunden. Wenn die SQL-Anweisung Parametermarker mit gleichen Namen hat, erscheint jedes Vorkommen in Params. Wenn Position für Parameter nicht festgelegt ist, wird es automatisch bei der Anweisungsvorbereitung mithilfe der Parameterindizes zugewiesen. Die Position beginnt bei 1.

Der Datentyp wird entweder explizit oder implizit durch Zuweisen eines Parameterwertes zur Eigenschaft Value oder AsXxxx festgelegt. Andernfalls wird FormatOptions.DefaultParamDataType verwendet.

Wenn ParamType nicht angegeben ist, wird ResourceOptions.DefaultParamType verwendet. Standardmäßig sind alle Parameter nur Eingabeparameter. Zur Verwendung von Ausgabeparametern muss ParamType explizit angegeben werden.

Die Eingabeparameterwerte müssen vor Ausführung der Abfrage festgelegt werden. Verwenden Sie dazu eine der folgenden Alternativen:

  • Weisen Sie Parameterwerte explizit zu:
FDQuery1.ParamByName('code').AsString := 'DA1001';
FDQuery1.Open;
  • Verwenden Sie eine der überladenen ExecSQL- oder Open-Methoden:
FDQuery1.Open('', ['DA1001']);

Um den Parameterwert auf Null zu setzen, geben Sie den Parameterdatentyp an und rufen dann die Methode Clear auf:

with FDQuery1.ParamByName('name') do begin
  DataType := ftString;
  Clear;
end;
FDQuery1.ExecSQL;

Auf die Werte der Ausgabeparameter können Sie nach Ausführung der Abfrage zugreifen. Bei einigen SQL-Anweisungen und DBMSs müssen Sie eventuell zuerst alle Ergebnismengen verarbeiten, erst dann können die Werte der Ausgabeparameter abgerufen werden. Mit der Methode TFDAdaptedDataSet.GetResults können Sie die Übergabe der Ausgabeparameterwerte anfordern. Lesen Sie auch den Artikel Einheitliche Unterstützung für RETURNING über Ausgabeparameter in der RETURNING-Klausel von Firebird und Oracle.

Vorbereiten der Abfrage

Bevor eine Abfrage ausgeführt wird, muss sie vorbereitet werden. Dazu führt FireDAC automatisch die folgenden Aktionen durch:

  • Versetzen der Verbindung in den aktiven oder Online-Status.
  • Überprüfen, ob alle Parameter korrekt festgelegt sind.
  • Vorverarbeiten des Anweisungstextes.
  • Optional Anwenden von RecsSkip und RecsMax für den Anweisungstext.
  • Optional Starten einer Transaktion für Firebird/InterBase-DBMSs, wenn keine aktive Verbindung vorhanden ist.
  • Übertragen des endgültigen Anweisungstextes an die DBMS-API. Der endgültige Text wird aus der Eigenschaft TFDQuery.Text abgerufen.

Während der Vorbereitung der Anweisung muss die Verbindung aktiv sein, und die Abfrage belegt die Serverressourcen. Verwenden Sie die Methoden Unprepare oder Disconnect, um die Vorbereitung der Abfrage aufzuheben und alle Ressourcen freizugeben.

Wenn eine Anweisung nur einmal ausgeführt werden muss, setzen Sie ResourceOptions.DirectExecute auf True. Bei einigen DBMSs (SQL Server, SQL Anywhere) wird dadurch die Ausführung beschleunigt, weil die zeitintensive Vorbereitung der SQL-Anweisung vermieden wird.

Ausführen der Abfrage

Mit den ExecSQL-Methoden führen Sie eine Abfrage aus, die keine Ergebnismenge zurückgibt. Wenn eine Abfrage eine Ergebnismenge zurückgibt, wird die Exception [FireDAC][Phys][MSAcc]-310. Anweisung, die Ergebnismengen zurückgibt, kann nicht ausgeführt werden ausgelöst.

Mit den Open-Methoden führen Sie eine Abfrage aus, die eine Ergebnismenge zurückgibt, und öffnen diese Ergebnismenge. Wenn eine Abfrage keine Ergebnismengen zurückgibt, wird die Exception [FireDAC][Phys][MSAcc]-308. Anweisung, die keine Ergebnismengen zurückgibt, kann nicht geöffnet/definiert werden ausgelöst.

Mit der Methode ExecuteOrOpen führen Sie eine Ad-hoc-Abfrage aus.

Hinweis: Die Abfrage kann asynchron ausgeführt werden. Wenn es sich bei der Abfrage um einen Anweisungsstapel handelt, finden Sie unter Anweisungsstapel Einzelheiten.

Die Exception Datentyp des Parameters wurde geändert

Wenn der Parametertyp nach dem ersten Aufruf von Prepare/ExecSQL/Open geändert wird, löst FireDAC beim folgenden Aufruf eine Exception aus:

[FireDAC][Phys][IB]-338. Parameter [Xxxx] data type is changed.
Query must be reprepared.

FireDAC speichert beim ersten Aufruf von Prepare/ExecSQL/Open den Datentyp der Parameter. Der Datentyp kann entweder explizit durch Setzen der Eigenschaft TFDParam.DataType oder implizit durch Zuweisen eines Wertes mit der Eigenschaft TFDParam.AsXxxx oder TFDParam.Value angegeben werden. Wenn die Anwendung den Parameterdatentyp vor dem nächsten Aufruf von ExecSQL/Open ändert, wird beim erneuten Aufrufen von ExecSQL/Open die Exception "Datentyp des Parameters [xxxxx] wurde geändert" ausgelöst.

Die Eigenschaft TFDParam.Value ändert den Parameterdatentyp beim nächsten Aufruf nicht. Sie wandelt den Typ des zugewiesenen Wertes in den aktuellen Datentyp um. Um die obige Exception zu vermeiden, verwenden Sie:

  • Den TFDParam.Value-Eigenschaftswert.
  • Die Eigenschaft TFDParam.AsXxxx, wenn Xxxx beim ersten Aufruf verwendet wurde.


Abrufen von DBMS-Rückmeldungen

Mit der Eigenschaft TFDQuery.RowsAffected rufen Sie die Anzahl der von der Anweisung verarbeiteten Zeilen ab (z. B. die Anzahl der durch die Anweisung DELETE gelöschten Zeilen).

Hinweis: Bei MS SQL Server kann RowsAffected gleich -1 sein, wenn bei einer gespeicherten Prozedur oder einem Tabellen-Trigger SET NOCOUNT ON weggelassen wird. Verwenden Sie dann die Eigenschaft TFDQuery.RecordCount zum Ermitteln der Anzahl der abgerufenen Zeilen.
Hinweis: FireDAC stellt keine Meldungen über die Anzahl der verarbeiteten Zeilen bereit. Bei Bedarf müssen solche Meldungen in der Anwendung erstellt werden.

Wenn die Anweisung einen Fehler zurückgibt, wird eine Exception ausgelöst. Weitere Einzelheiten finden Sie unter Fehlerbehandlung. Die Exception kann auf eine der folgenden drei Arten verarbeitet werden:

  • Verwenden der Konstruktion "try/except/end". Zum Beispiel:
try
  FDQuery1.ExecSQL;
except
  on E: EFDDBEngineException do
    ; // do something here
end;
procedure TForm1.FDConnection1Error(ASender: TObject; const AInitiator: IFDStanObject;
  var AException: Exception);
begin
  if (AException is EFDDBEngineException) and (EFDDBEngineException(AException).Kind = ekRecordLocked) then
    AException.Message := 'Please, try the operation later. At moment, the record is busy';
end;

FDConnection1.OnError := FDConnection1Error;

Bei der Array DML ist die Fehlerbehandlung komplexer.

Die Anweisung gibt je nach DBMS Warnungen, Hinweise und Meldungen zurück. Setzen Sie ResourceOptions.ServerOutput auf True, um die Meldungsverarbeitung zu aktivieren. Mit der Eigenschaft TFDConnection.Messages werden die Meldungen verarbeitet. Zum Beispiel:

var
  i: Integer;
begin
  FDConnection1.ResourceOptions.ServerOutput := True;
  FDQuery1.ExecSQL;
  if FDConnection1.Messages <> nil then
    for i := 0 to FDConnection1.Messages.ErrorCount - 1 do
      Memo1.Lines.Add(FDConnection1.Messages[i].Message);
end;

Einige DBMSs, wie SQL Server, geben Meldungen als weitere Ergebnismenge zurück. Daher muss die Anwendung zum Verarbeiten von Meldungen mehrere Ergebnismengen verarbeiten. Im Folgenden finden Sie ein komplexeres Beispiel, das den Status und die Meldungen für SQL Server bereitstellt. In diesem Beispiel wird TFDMemTable verwendet, um Ergebnismengen mit Zeilen zu speichern.

var
  i: Integer;
begin
  FDConnection1.ResourceOptions.ServerOutput := True;
  FDQuery1.FetchOptions.AutoClose := False;
  FDQuery1.Open('select * from Region; print ''Hello''');
  FDMemTable1.Data := FDQuery1.Data;
  Memo1.Lines.Add(Format('%d rows processed', [FDMemTable1.RecordCount]));
  FDQuery1.NextRecordSet;
  if FDConnection1.Messages <> nil then
    for i := 0 to FDConnection1.Messages.ErrorCount - 1 do
      Memo1.Lines.Add(FDConnection1.Messages[i].Message);
end;


Abfrageausführung und Transaktionen

Standardmäßig werden alle SQL-Anweisungsausführungen im Modus "Autocommit" durchgeführt. Das heißt, dass diese implizite Transaktion gestartet wird, wenn direkt vor der Anweisungsausführung keine aktive Transaktion vorhanden ist. Ebenso wird die Transaktion direkt nach der Anweisungsausführung beendet:

  • durch Commit bei erfolgreicher Ausführung.
  • durch Rollback bei einem Fehler.

Bei Firebird und InterBase startet eine implizite Transaktion direkt vor der Abfragevorbereitung. Eine einzelne Anweisungsausführung muss nicht in explizite Transaktionen eingeschlossen sein, verwenden Sie einfach den Modus "Autocommit".


Erweiterte Optionen

Die meisten Datenbankanwendungen verfügen für die Verwaltung über Backend-Hilfsprogramme, die SQL-Skripte ausführen müssen. Die Zeilen in diesen Skripten entsprechen in gewisser Weise der DBMS-SQL-Skriptsyntax. Verwenden Sie zum Ausführen von SQL-Skripten die Komponente TFDScript.

Manchmal führt eine Datenbankanwendung heterogene Abfragen aus, indem Tabellen aus verschiedenen Datenbanken verwendet werden, oder sie führt SQL-Anweisungen für TDataSet-Nachkommen anstatt für die Datenbanktabellen aus. Verwenden Sie die Local SQL-Engine und die Komponente TFDLocalSQL, um solche Abfragen auszuführen.

Siehe auch