Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x) (https://www.delphipraxis.net/211351-firedac-den-autoinc-value-onbeforepost-abfragen-ist-immer-x.html)

Kostas 2. Sep 2022 16:07

Datenbank: Firebird • Version: 3.0 • Zugriff über: FIREDAC

FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Hallo Zusammen,

ich benötige von einer fdQuery bei onBeforePost den Generator Wert des PKs.
Bei onBeforePost ist der Wert immer -1, -2, -3 bei jedem Insert.

Eingestellt haben ich in der fdQuery:

UpdateOptions.AssignedValues = [uvFetchGeneratorsPoint,uvGeneratorName]
UpdateOptions.AutoIncFields = bankdatenschulungenid
UpdateOptions.FetchGeneratorsPoint = gpImmediate
UpdateOptions.GeneratorName = gen_bankdatenschulungen_id
UpdateOptions.KeyFields = bankdatenschulungenid

Hat jemand eine Idee was ich übersehe?

Gruß Kostas

Uwe Raabe 2. Sep 2022 16:24

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Und bankdatenschulungenid ist auch vom Typ
Delphi-Quellcode:
TFDAutoIncField
?

Uwe Raabe 2. Sep 2022 16:29

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Und falls ja, wie ist
Delphi-Quellcode:
ClientAutoIncrement
eingestellt?

Kostas 2. Sep 2022 16:35

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Hallo Uwe,

ich hatte der Query die Felder im Feld-Editor NICHT angelegt da ich sie nicht benötige. Habe ich gerade nachgeholt zum testen. Das Feld wird als TFDAutoIncField erkannt. Dennoch wird -1 bei onBeforePost zurückgegeben. Nach dem Post wird das Feld aktualisiert und bekommt den richtigen Wert.

Kostas 2. Sep 2022 16:37

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
ClientAutoIncrement ist auf true gesetzt.

Kostas 2. Sep 2022 16:43

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Mir ist noch etwas aufgefallen:

Ich arbeite wie gesagt ohne die Feldobjekte.
Zum testen habe ich bei AfterIinsert den Wert 1000 übergeben.
Bei OnBeforePost habe ich die 1000.
Nach dem Post wird die 1000 durch den Generatorwert überschrieben über den Trigger in der DB.
Das bedeutet, die Query senden den vorbelegten PK nicht zur DB.


Delphi-Quellcode:
procedure Tfr1.qrBankdatenSchulungAfterInsert(DataSet: TDataSet);
begin
  qrBankdatenSchulung.FieldByName('BANKDATENSCHULUNGENID').AsInteger := 1000;
end;

Delphi-Quellcode:
CREATE OR ALTER trigger bankdatenschulungen_bi for bankdatenschulungen
active before insert position 0
as
begin
  if (new.bankdatenschulungenid is null) then
    new.bankdatenschulungenid = gen_id(gen_bankdatenschulungen_id,1);
end

haentschman 2. Sep 2022 16:46

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Zitat:

Nach dem Post wird das Feld aktualisiert und bekommt den richtigen Wert.
...und so muß das sein. Wie kann die Query den Wert "erhalten" wenn der vom Trigger nicht generiert wurde? :gruebel:

Deswegen heiß das BeforePost...:zwinker:

Uwe Raabe 2. Sep 2022 16:47

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Zitat:

Zitat von Kostas (Beitrag 1511160)
ClientAutoIncrement ist auf true gesetzt.

Dann greift die Zählung mit den negativen Werten und erst beim Post werden die tatsächlichen IDs ermittelt. Setz das mal auf False.

Uwe Raabe 2. Sep 2022 16:47

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Zitat:

Zitat von haentschman (Beitrag 1511162)
Wie kann die Query den Wert "erhalten" wenn der vom Trigger nicht generiert wurde? :gruebel:

Na, dafür hat er ja den Generatornamen angegeben.

Kostas 2. Sep 2022 16:51

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1511163)
Zitat:

Zitat von Kostas (Beitrag 1511160)
ClientAutoIncrement ist auf true gesetzt.

Dann greift die Zählung mit den negativen Werten und erst beim Post werden die tatsächlichen IDs ermittelt. Setz das mal auf False.

habe ich gerade, leider ohne Erfolg.

haentschman 2. Sep 2022 16:52

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Zitat:

active before insert position 0
...das heißt in der DB auch Before. Ist mir noch nicht untergekommen. :gruebel: Für mein Verständis brauche ich die neue "ID" erst dann, wenn der Datensatz geschrieben wurde.

himitsu 2. Sep 2022 17:02

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Was vom Trigger kommt, dass kann FireDAC garnicht wissen, da es vor ihm versteckt ist und es auch erst bei POST in der DB ausgeführt wird.

Wie wäre es mit
Delphi-Quellcode:
generated by
?
https://stackoverflow.com/questions/...ebird-database


Natürlich kannst du im OnAfterInsert ein
SQL-Code:
SELECT gen_id(gen_bankdatenschulungen_id,1)
machen, die ID abfragen und deinem Field zuweisen.



Bei PostgreSQL stehen die Generatoren mit in den DEFAULT-Werten Felder.
Im AfterInsert wird bei uns ein SELECT mit den Defaults zusammengebaut (die SQL-Statements davon kann man ja von der DB abfragen), ausgeführt und den Fields zugewiesen,
damit die Nutzer bereits in den Edits/Grids die Werte sehen, welche sonst eigentlich erst beim POST reinkommen kommen würden.

Uwe Raabe 2. Sep 2022 19:36

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Der Kernpunkt ist hier
Delphi-Quellcode:
UpdateOptions.FetchGeneratorsPoint = gpImmediate
. Das sorgt laut Dokumentation dafür, dass die ID schon beim Insert bzw. Append ermittelt wird.
Zitat:

Controls the moment when the next generator value is fetched.

Use FetchGeneratorsPoint to specify the moment when the next generator value is fetched from a database and assigned to an auto-incremental field. The default value is gpDeferred.

The next generator value will be fetched from a generator, specified by the TFDUpdateOptions.GeneratorName/TFDAutoIncField.GeneratorName properties and assigned to the auto-incremental fields, which have pfInUpdate in ProviderFlags.

An auto-incremental field is:

A field of the TFDAutoIncField class.
A field whose name is specified in AutoIncFields.
A field with TField.AutoGenerateValue = arAutoInc.
Generators are supported by InterBase and Firebird. Sequences are supported by Oracle and PostgreSQL DBMSs. For other DBMSs this property has no meaning. The value can be one of the following:

Code:
Mode         Description

gpNone       Do not fetch generator values.
gpImmediate  Fetch values right after starting to append a new record. Normally, this is the value for the TDataSet.Insert or Append methods.
gpDeferred   Fetch values right before posting a new record to a database. Normally, this is the value for the TDataSet.Post or ApplyUpdates methods.


jobo 3. Sep 2022 13:43

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Ein AutoInc Field zur Befüllung eines PK wird aus Sicht der DB entweder über
  • den Spaltentyp geregelt oder
  • über einen Trigger mit Hilfe eines Generators oder
  • nur über einen Generator.

Anscheinend gibt es in
<=V. 2.5:
nur Generatoren, diese können mit Triggern zur Befüllung kombiniert werden
V. 3:
die Spaltendefinition "by default as identity primary key"
V. 4:
die Spaltendefinition "always as identity primary key"

zu "<=2.5" bzw. überall dort wo ein Trigger im Einsatz ist:
Die Existenz und Nutzung eines Triggers sagt nichts über die zwangsweise Nutzung des Generatorwertes. Trigger sind häufig so geschrieben, dass sie einen mitgegebenen Wert (durch das Insert) verwenden. (siehe unten)

zu 3: "..by default.." verhält sich analog zum häufig so wie oben beschriebenen Triggeraufbau, es setzt nur den Defaultwert mittels einer (internen) Sequenz, dieser bleibt aber überschreibbar. Also gleiche Funktionalität wie zuvor, nur ohne den selbst erstellten Trigger.

zu 4: ".. always .." verwendet immer den internen Sequenzwert, benötigt auch keinen Trigger.

Nach der Befüllung nutzt man die Rückgabe/Abfrage des Generators zur Weiterverwendung dieses Wertes bspw. in FKs. Wird ein Generator verwendet, dann per fragt man den letzten Wert so ab:
Code:
SELECT GEN_ID( GeneratorName, 0 ) FROM RDB$DATABASE;
(oder innerhalb eines anderen SQL Statements)
Seit FB 2.0 kann auch gleich per RETURNING im Insert Statement der Wert zurückgegeben werden.
Das ist der sicherste und bequemste Weg, diesen Wert zu erfahren, weil er unabhängig von der Erzeugung ist.

Das bis jetzt beschriebene Verhalten muss sich alles per SQL in der Kommandozeile nachvollziehen lassen (Es muss natürlich entsprechend in den Tabellen richtig definiert sein).

Die FD Kompos und ihr Verhalten kenne ich nicht. (Was ich hier schreibe, habe ich nicht in Delphi validiert) Sie müssen aber alles oben beschriebene berücksichtigen und können nicht tiefer eingreifen, als es mit bloßem SQL geht. Sie bieten aber "Komfortproperties" zur einfacheren Handhabung.
Wenn man in den Properties einen Generatornamen angeben kann, dann sollte es für das gewünschte Verhalten auch gemacht werden.
Ab FB 3 sollte es nicht nötig sein, einen Generator anzulegen, wenn die Spalte mit "..generated.." definiert wurde. (Mir ist nicht klar, ob die FD Kompos das 'generated' Feature überhaupt berücksichtigen)

Zeitpunkt (Before/After- für DB und Delphi)
Alles was die DB macht (an Werten ändert/ergänzt), kann Delphi (im Prinzip egal welcher Client) nicht wissen, solange das Insert nicht abgeschlossen ist. (Ausnahme, es gibt keine Trigger, keine Generated Spalten und auch keine Defaults, dann ist bei einem simplen Insert aus dem Client auch das Ergebnis bekannt, weil identisch). Eine zeitliche Unterscheidung gibt es dabei nur mittels Trigger. Das wird ja auch beim Insert Trigger "before insert" für "new.IDcolumn" genutzt.

Ein Client kann eigentlich nur 2 Dinge tun:
Entweder er holt selbst einen neuen Generatorwert, bevor das Insert erfolgt und übergibt den Wert an das Insert.
Oder er holt nach dem Insert den aktuellen Generatorwert und stellt ihn innerhalb der Komponente zur Weiterverwendung (Anzeige, ..) zur Verfügung.

Wenn man nach dem Insert einen Wert aus dem Client überschreibt- wie @kostas es darstellt-, sollte der auch gezielt gepostet werden, sonst geschieht das Posten gar nicht oder ungezielt irgendwann implizit, man zerstört zu einem ungewissen Zeitpunkt also den Originalwert. Damit verlässt man die Vorgaben, die DB Seitig festgelegt wurden. Es handelt sich aus DB Perspektive dann um ein Update.

Der von @kostas gepostete Trigger
[code]
CREATE OR ALTER trigger bankdatenschulungen_bi for bankdatenschulungen
active before insert position 0
as
begin
if (new.bankdatenschulungenid is null) then
new.bankdatenschulungenid = gen_id(gen_bankdatenschulungen_id,1);
end
[code]
setzt die ID new.bankdatenschulungenid nur unter der Bedingung, dass das Feld nicht bereits vorbelegt ist(oder auch mitgeliefert wurde- normalerweise durch das Insertstatement). Hat die Spalte also später einen Wert, den man separat angegeben hat, ist es in diesem Fall dem bedingt arbeitenden Trigger zu verdanken und hat erstmal nichts mit Client Komponenten zu tun. Oder der separate Wert kommt durch ein Update, dann ist der Insert Trigger sowieso nicht beteiligt.

Ich habe dazu fiddles angelegt. Diese klären natürlich nicht das Verhalten im Client, verdeutlichen aber hoffentlich so gut es geht das DB Verhalten- welches unumgänglich ist. Bei Interesse bitte Bescheid sagen.

Uwe Raabe 3. Sep 2022 16:01

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Deine Beschreibungen sind zwar gut fundiert und umfangreich, decken aber das im Originalpost verwendete Verfahren nicht ab. Dabei werden die IDs zwar über einen Generator in der DB erzeugt, aber von der Client-Komponente an den Server übertragen. Das hat den Vorteil, und genau darum geht es hier gerade, dass die IDs dem Programm schon vor dem Speichern des Datensatzes in der DB bekannt sind.

Grundsätzlich ist das ein von FireDAC unterstütztes Vorgehen. Ob man es selbst anders realisieren würde steht nicht zur Debatte, weil wir die konkreten Anforderungen nicht kennen.

Es geht also schlichtweg darum, ob und wenn ja welche einzelne Einstellungen in den Komponenten nicht korrekt sind oder ob womöglich ein Bug in der verwendeten Delphi-Version vorliegt, der in späteren Versionen behoben wurde.

jobo 3. Sep 2022 16:36

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1511193)
..Dabei werden die IDs zwar über einen Generator in der DB erzeugt, aber von der Client-Komponente an den Server übertragen. Das hat den Vorteil, und genau darum geht es hier gerade, dass die IDs dem Programm schon vor dem Speichern des Datensatzes in der DB bekannt sind.

Naja, das wollte ich hiermit zum Ausdruck bringen:

Zitat:

Ein Client kann eigentlich nur 2 Dinge tun:
Entweder er holt selbst einen neuen Generatorwert, bevor das Insert erfolgt und übergibt den Wert an das Insert.
Oder er holt nach dem Insert den aktuellen Generatorwert und stellt ihn innerhalb der Komponente zur Weiterverwendung (Anzeige, ..) zur Verfügung.
Ich hoffe, die Beschreibung dient ansonsten dem allgemeinen Verständnis.

Kostas 3. Sep 2022 16:38

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1511175)
Der Kernpunkt ist hier
Delphi-Quellcode:
UpdateOptions.FetchGeneratorsPoint = gpImmediate
. Das sorgt laut Dokumentation dafür, dass die ID schon beim Insert bzw. Append ermittelt wird.

...
Code:
Mode         Description

gpNone       Do not fetch generator values.
gpImmediate  Fetch values right after starting to append a new record. Normally, this is the value for the TDataSet.Insert or Append methods.
gpDeferred   Fetch values right before posting a new record to a database. Normally, this is the value for the TDataSet.Post or ApplyUpdates methods.

[/QUOTE]


Die Doku habe ich gelesen und so eingestellt, meine ich zumindest. Ich hätte erwartet dass bei onBeforePost den Generatorwert bereits gesetzt ist. Genau das ist nicht der Fall. Auch interessant ist, dass das PK Feld, auch wenn ich den Wert setze, in der DB immer als null übertragen wird. Deshalb feuert der Trigger der DB und setzt den PK Wert. Ich habe ein Demo Programm erstelle auf die gleiche DB und die Connection über IBObjects erstellt. Hier ist der Generator des PKs bei OnBeforePost vorhanden und auch so zur DB übertragen. Es liegt also nicht an der DB sondern an irgend einer Einstellung in FireDac. Ich verwende übrigens Delphi 11.1

himitsu 3. Sep 2022 17:05

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Wie schon gesagt ... das KANN auch garnicht vom FireDAC hier so gemacht werden.

Ob und was IN einer Trigger-Funktion drin steht, dass weiß es nicht und kann es auch nicht sicher wissen (Funktionsinhalt parsen und Funktionsweise analysieren), also kann es auch nicht im AfterInster genutzt werden.

Nur was AN dem Tabellen-Feld deklariert ist, dass kann es auslesen und verwenden.

Bei dem "always" könnte es zwas die Sqeuenz abfragen, aber es wäre "Sinnlos", weil es beim Speichern wieder überschrieben würde. (falls zwischendurch jemand anderes was gemacht hat, dann auch noch mit einem anderen Wert)

jobo 3. Sep 2022 18:02

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Wie gesagt, es ging mir um die DB Seite. Wenn (dem Nutzer) klar ist, wie das funktioniert, klärt sich vielleicht auch die Client Seite.
Wenn man die "generated" Variante wählt, braucht es eh keinen Trigger und keine eigenen Generatoren. Und die Sequenzen, die in dem Fall dann genutzt werden, sind ja erstmal gar nicht transparent.
"Generated" kann dadurch am ehesten mit Returning Clause genutzt werden.
Aber vielleicht beherrschen das die FD Kompos auch, weiß ich nicht, möglich wäre es.

Uwe Raabe 3. Sep 2022 18:31

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Zitat:

Zitat von Kostas (Beitrag 1511196)
Auch interessant ist, dass das PK Feld, auch wenn ich den Wert setze, in der DB immer als null übertragen wird.

Das ist zumindest ein Hinweis, wo das Problem liegen könnte.

Kannst du ein kleines Beispielprogramm mit denselben FireDAC Einstellungen machen?

Kostas 6. Sep 2022 09:58

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Liste der Anhänge anzeigen (Anzahl: 2)
Hallo Zusammen,

Anbei ein Demo Projekt für FB 3.0.

ich habe es zuerst ohne die Felder in den Feldeditor zum laden ausprobiert. Danach mit, ist kein Unterschied.
Es geht darum, ich hätte gerne den neuen abgefragten PK bei onBeforePost.

Das nächste Problem, wenn ich den PK vorbelege z.B. 1000 wird zur DB null gesendet. Somit feuert der Triggere und setzt den nächsten Zähler.

vielen Dank.

Uwe Raabe 6. Sep 2022 10:27

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Das Problen ist das fehlende pfInUpdate ProviderFlag.
Delphi-Quellcode:
  qrTable1.open;
  var fldID := qrTable1.FieldByName('TABLE1ID');
  fldID.ProviderFlags := fldID.ProviderFlags + [pfInUpdate];
Das steht auch in der weiter oben verlinkten Dokumentation (von mir fett hervorgehoben):
Zitat:

The next generator value will be fetched from a generator, specified by the TFDUpdateOptions.GeneratorName/TFDAutoIncField.GeneratorName properties and assigned to the auto-incremental fields, which have pfInUpdate in ProviderFlags.

Uwe Raabe 6. Sep 2022 10:31

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Du kannst auch das ID-Feld statisch anlegen und dort die passenden Einstellungen machen. Um dann auch noch die anderen Felder dynamisch zu erhalten musst du in der Query unter FieldOptions das AutoCreateMode auf acCombineAlways setzen.

Kostas 6. Sep 2022 10:38

AW: FireDac den AutoInc value in onBeforePost abfragen. (ist immer -x)
 
Hallo Uwe,

wie immer, es funktioniert.

Besten Dank und einen schönen Tag.


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:54 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz