Fragen zum Bearbeiten (FireDAC)
Nach oben zu FAQ (FireDAC)
Dieses Thema enthält eine Liste mit Fragen und Antworten zum Bearbeiten von Daten.
F1: Was bedeutet "[FireDAC][DApt]-400. Update-Anweisung hat [0] anstatt [1] Datensätze aktualisiert"?
A: Dieser Fehler wird häufig ausgelöst, wenn FireDAC Tabellenfelder mit dem Datentyp Float/Double/Single/Date/Datetime/Time oder andere einbezieht. In WHERE-Klauseln verlieren diese Felder an Genauigkeit. Wenn in einer Anwendung Werte an Parameter zugewiesen werden, kann ein Genauigkeitsverlust auftreten. Aufgrund dessen gibt die WHERE-Klausel keine Datensätze zurück.
FireDAC kann abhängig von UpdateOptions.UpdateMode derartige Felder in WHERE-Klauseln enthalten. Manchmal tritt dieser Fehler bei upWhereKeyOnly auf. Auch wenn Sie upWhereKeyOnly angegeben haben, könnte FireDAC trotzdem upWhereAll verwenden. Dies kommt vor, wenn kein Primärschlüsselfeld definiert ist. Keine Primärschlüsselfelder können definiert sein, wenn:
- fiMeta aus FetchOptions.Items ausgeschlossen ist.
- Oder für eine Tabelle in SQL kein Primärschlüsselfeld definiert ist.
- Oder UpdateOptions.KeyFields leer ist.
- Oder pfInKey für kein TField in ProviderFlags angegeben ist.
Ein weiterer Grund ist bei einigen DBMSs (SQL Server, PostgreSQL), dass die Tabelle einen Trigger hat, der die Daten ändert. Setzen Sie für SQL Server am Beginn des Triggers SET NOCOUNT ON. Setzen Sie für PostgreSQL UpdateOptions.CountUpdatedRecords auf False.
F2: Wann muss TFDUpdateSQL verwendet werden?
A: FireDAC generiert SQL-Aktualisierungsanweisungen automatisch, wenn die ursprüngliche SQL-Anweisung eine einfache SELECT- oder eine SELECT-Anweisung mit JOIN ist, wobei die Primärschlüsselfelder in einer einzelnen Tabelle gespeichert sind. Daher ist die Verwendung von TFDUpdateSQL optional. TFDUpdateSQL ist erforderlich, wenn:
- Die ursprüngliche SQL-Anweisung keine SELECT-Anweisung ist (z. B. eine gespeicherte Prozedur, die Ergebnismengen zurückgibt).
- Die ursprüngliche SQL-Anweisung den Primärschlüssel nicht beibehält (z. B. Verknüpfung mehreren Tabellen mit DISTINCT- oder GROUP BY-Klauseln).
- Eine Anwendung SQL-Aktualisierungsanweisungen benötigt, die nicht dem Standard entsprechen (z. B. die Anwendung trägt Aktualisierungen über Aufrufe von gespeicherten Prozeduren ein).
F3: Können Makros im SQL-Code in FDUpdateSQL verwendet werden?
A: Verwenden Sie den folgenden Code:
FDUpdateSQL1.Commands[arInsert].MacroByName('MacroName').Value := 'value';
F4: Warum wird beim mehrmaligen Aufruf von ApplyUpdates im CachedUpdates-Modus versucht, eingefügte Datensätze erneut einzutragen?
A: Nach dem Aufruf von ApplyUpdate sollten Sie CommitUpdates aufrufen. Nach diesem Aufruf werden alle Änderungen aus dem internen Zwischenspeicher gelöscht.
F5: Wie kann die Aktualisierung einer Detail-TFDQuery verhindert werden, nachdem in einer Haupt-TFDQuery ein Bildlauf durchgeführt wurde oder Eintragungen vorgenommen wurden?
A: Hierfür gibt es zwei Möglichkeiten:
- Implementieren Sie eine eigene Haupt/Detail-Verknüpfung. Dazu müssen Sie die Ereignisbehandlungsroutine TDataSource.OnDataChange hinzufügen. Dies ist der "Standardweg".
- Verwenden Sie zentralisierte zwischengespeicherte Aktualisierungen.
F6: Wie werden Datenmengendatensätze entfernt, ohne sie aus der Datenbank zu entfernen?
A: Sie können direkt mit dem internen Datenspeicher der Datenmenge arbeiten. Mit der Eigenschaft TFDDataSet.Table greifen Sie darauf zu. Gehen Sie folgendermaßen vor, um z. B. die Zeile mit dem Index 3 zu löschen:
FDQuery1.Table.Rows[3].Free;
FDQuery1.UpdateCursorPos;
FDQuery1.Resync([]);
Gehen Sie folgendermaßen vor, um z. B. den aktuellen Datensatz zu löschen:
FDQuery1.UpdateCursorPos;
FDQuery1.GetRow.Free;
FDQuery1.UpdateCursorPos;
FDQuery1.Resync([]);
Und schließlich können Sie dem CachedUpdates-Modus verwenden. Setzen Sie eine Datenmenge in den Modus "Zwischengespeicherte Aktualisierungen", löschen Sie dann einen Datensatz, und rufen Sie CommitUpdates auf.
F7: Wie kann ich mit FireDAC "ATable.UpdateToDataset(BTable , 'mykey', [mtufEdit, mtufAppend])" erstellen?
A: Verwenden Sie die Methode TFDDataSet.CopyDataSet mit den folgenden Optionen:
- [coAppend] – Alle Datensätze aus ASource (unverändert) anhängen.
- [coEdit] – Nur Datensätze mit Schlüsselwerten bearbeiten.
- [coAppend, coEdit] – Datensätze mit Schlüsselwerten bearbeiten und Datensätze ohne bestehende Schlüsselwerte anhängen.
F8: Wie kann ich einem ftGUID-Feld einen Wert hinzufügen?
A: Verwenden Sie den folgenden Code:
(AMemTable.FieldByName('Field1') as TGUIDField).AsGuid := aGUID;
F9: Wie kann ich einen Vorgabewert für ein Datenmengenfeld festlegen?
A: Weisen Sie der Eigenschaft TField.DefaultExpression einen Ausdruck zu.
F10: Wird die Eigenschaft TField.DefaultExpression genauso wie TField.CustomConstraint unterstützt, und wird der Ausdruck als Vorgabewert für ein Feld übernommen?
Ja, wenn das Feld ein normales Feld einer Ergebnismenge ist. Wenn ein Feld als fkInternalCalc definiert ist, dann wird das Ergebnis von DefaultExpression als Feldwert verwendet und wie jedes andere berechnete Feld aktualisiert.
F11: Ist der Ausdruck {fn DAYOFMONTH({fn CURDATE()})} richtig?
A: Nein, Sie verwenden FireDAC-Escape-Funktionen. Escape-Funktionen werden nur in SQL-Anweisungen, nicht in Ausdrücken unterstützt. In Ausdrücken (z. B. für Bedingungen und Vorgabewerte) müssen Sie die Funktionen und die Syntax verwenden, die von der FireDAC-Ausdrucksauswertung unterstützt werden:
DAYOFMONTH(CURDATE())
Um solche Funktionen zu verwenden, müssen Sie auch die Unit uADStanExprFuncs in Ihre Anwendung einbeziehen.
F12: Wie kann ich den Vorgabewert für ein boolesches Datenmengenfeld angeben?
A: Weisen Sie den gewünschte Ausdruck der Eigenschaft TField.DefaultExpression zu, um einen Vorgabewert für ein Datenmengenfeld festzulegen.
Zum Zuweisen eines Wertes zu einem booleschen Feld können Sie die folgenden Strings verwenden: F, FA, FAL, FALS, FALSE als Werte für "Falsch". Und analog für True.
F13: Die Zuweisung von TField.CustomConstraint funktioniert nicht. Was ist falsch?
F: Das Anhängen einer Bedingung über:
FDQuery.FieldByName('FIELD_NAME').CustomConstraint := 'FIELD_NAME > 1';
FDQuery.UpdateConstraints;
FDQuery.Table.Constraints.Check(FDQuery.GetRow(), rsModified, ctAtEditEnd);
funktioniert nicht, und es wird auch keine Exception ausgelöst.
A: Das ist OK (Erklärung folgt).
F: Aber:
FDQuery.Constraints.Add.CustomConstraint := 'FIELD_NAME > 1';
FDQuery.UpdateConstraints;
FDQuery.Table.Constraints.Check(FDQuery.GetRow(), rsModified, ctAtEditEnd);
Funktioniert! Warum?
A: Auch OK.
F: Was bedeuten ctAtEditEnd und ctAtColumnChange genau?
A: Diese Aufzählungen geben das Ereignis dafür an, wann FireDAC Bedingungen prüfen soll:
- ctAtEditEnd – Post wird aufgerufen
- ctAtColumnChange – ein Feldwert wird geändert
Nun die Erklärung:
FDQuery.FieldByName('FIELD_NAME').CustomConstraint := 'FIELD_NAME > 1';
Damit wird eine Bedingung auf Feldebene hinzugefügt. Diese Bedingung wird nur beim Ereignis ctAtColumnChange überprüft.
FDQuery.Constraints.Add.CustomConstraint := 'FIELD_NAME > 1';
Damit wird eine Bedingung auf Datensatzebene hinzugefügt. Diese Bedingung wird nur beim Ereignis ctAtEditEnd überprüft.