Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   FireDAC : Trigger geht nicht (https://www.delphipraxis.net/186469-firedac-trigger-geht-nicht.html)

Hansa 4. Sep 2015 13:38

Datenbank: Firebird • Version: 2.5 • Zugriff über: FireDAC

FireDAC : Trigger geht nicht
 
Moin,

will etwas mit FireDac testen und wollte mir deshalb eine Table erstellen.

Delphi-Quellcode:
    DM.DS.Insert;
    DM.DS.FieldByName('NR').AsInteger := i + 100;
    DM.DS.FieldByName('BEZ').AsInteger := i;
    DM.DS.Post;
    if i mod 1000 = 0 then begin
mem1.Lines.Add(IntToStr (i));
      DM.Trans.Commit;
      DM.Trans.StartTransaction;
    end;
Die Table enthält natürlich noch eine ID, die über Trigger hochgezählt wird. Lege ich in IBExpert von Hand einen Datensatz an, ähnlich wie hier im Programm, dann wird die nicht eingegebene ID automatisch vergeben, der Trigger scheint also zu gehen. Lasse ich obiges Delphi-Programm laufen, dann kommt allerdings folgende Fehlermeldung : "Das Feld ID muss einen Wert haben". Wieso das ?

Nachtrag : ersetze ich das Insert durch ein Edit, dann laufen in dem Kontrollmemo schön die Tausender durch, das Feld BEZ bleibt aber bei dem vorher manuell eingegeben Wert. Da wird also in diesem Fall nichts abgespeichert, ohne dass Fehlermeldung kommt.

mkinzler 4. Sep 2015 13:50

AW: FireDAC : Trigger geht nicht
 
Welche eine Komponente? Wie sieht der Trigger aus?

FireDAC führt keinen Trigger aus, sondern der Server.

Hansa 4. Sep 2015 13:59

AW: FireDAC : Trigger geht nicht
 
Zitat:

Zitat von Hansa (Beitrag 1314732)
Die Table enthält natürlich noch eine ID, die über Trigger hochgezählt wird. Lege ich in IBExpert von Hand einen Datensatz an, ähnlich wie hier im Programm, dann wird die nicht eingegebene ID automatisch vergeben

Was soll man denn sonst noch zu dem Trigger sagen ? Also nochmals : der geht und scheidet als Fehler aus. Wie "Welche eine Komponente" ??? :shock:

haentschman 4. Sep 2015 14:03

AW: FireDAC : Trigger geht nicht
 
Hallo Hansa,

zeig mal den Trigger. Was ist wenn du nicht mit Insert/Post arbeitest sondern wie jeder Normale (Spaß :P) eine Query absetzt?

mkinzler 4. Sep 2015 14:06

AW: FireDAC : Trigger geht nicht
 
Zitat:

Was soll man denn sonst noch zu dem Trigger sagen ? Also nochmals : der geht und scheidet als Fehler aus.
Ich glaube auch, dass der Trigger aufgerufen wird, ob die Bedingung aber greift kann ich ohne zu wissen wie er aussieht nicht.
Zitat:

Wie "Welche eine Komponente" ???
FireDAC hat eine Unmenge von Komponenten. Zumidest bei mir, bei Dir scheint es nur eine zu geben ( Table?)

nahpets 4. Sep 2015 14:12

AW: FireDAC : Trigger geht nicht
 
Bin mal wieder naiv, was passiert denn bei:
Delphi-Quellcode:
   DM.DS.Insert;
    DM.DS.FieldByName('ID').AsInteger := 0;
    DM.DS.FieldByName('NR').AsInteger := i + 100;
    DM.DS.FieldByName('BEZ').AsInteger := i;
    DM.DS.Post;
    if i mod 1000 = 0 then begin
mem1.Lines.Add(IntToStr (i));
      DM.Trans.Commit;
      DM.Trans.StartTransaction;
    end;
Wenn ID als not null definiert ist, so fehlt beim Insert für die ID natürlich ein Wert. Der Trigger schlägt (vermutlich) erst nach dieser Überprüfung zu.

Uwe Raabe 4. Sep 2015 14:12

AW: FireDAC : Trigger geht nicht
 
Zitat:

Zitat von Hansa (Beitrag 1314732)
Lasse ich obiges Delphi-Programm laufen, dann kommt allerdings folgende Fehlermeldung : "Das Feld ID muss einen Wert haben". Wieso das ?

Eigentlich erkennt FireDAC solche AutoInc-Felder ganz gut. Ist denn bei dir das ID-Feld auch ein TFDAutoIncField? Wenn nicht, lass es einfach nochmal im Feldeditor erzeugen. Sollte es nicht erkannt werden, musst du eventuell selbst Hand anlegen: Auto-Inkrementierungsfelder (FireDAC)


Zitat:

Zitat von Hansa (Beitrag 1314732)
Nachtrag : ersetze ich das Insert durch ein Edit, dann laufen in dem Kontrollmemo schön die Tausender durch, das Feld BEZ bleibt aber bei dem vorher manuell eingegeben Wert. Da wird also in diesem Fall nichts abgespeichert, ohne dass Fehlermeldung kommt.

Na ja, bei einem Edit bearbeitest du ja auch immer denselben Datensatz, der ja auch schon eine ID hat.

Hansa 4. Sep 2015 14:51

AW: FireDAC : Trigger geht nicht
 
Jetzt zum dritten mal, ein und derselbe Trigger der in IBExtert offensichtlich zuschlägt, schlägt offensichtlich über FireDAC nicht zu.

Code:
  IF (NEW.ID IS NULL) THEN
    NEW.ID = GEN_ID(GEN_TEST_ID, 1);
Das steht im Trigger, was sonst ? Wenn ich schreibe Table dann ist zumindest aus dem Zusammenhang zu schliessen, dass damit Database-Table gemeint ist und nicht irgendeine Table-Komponente. DM steht übrigens für DataModule und DS für Dataset, bzw. in diesem Fall für FDQuery. Dass es viele FireDac-Komponenten gibt weiss ich auch und ich benutze nicht nur die eine. 8-)

@nahpets : das habe ich auch schon probiert, also die ID von Hand vergeben. Aber auch dann kommt offensichtlich nichts in der DB an. Fehlermeldung bleibt aber aus.

@Uwe : beim Edit ist die ID ja schon da und der Trigger tritt nicht in Aktion, ist ja klar, aber ich ändere doch zumindest die anderen beiden Felder. Auch davon ist nichts zu sehen.

Das dürfte alles nicht viel sein, irgendeine dumme Einstellung, aber momentan ein Rätsel. Mir fällt langsam nichts mehr ein, wo ich nachgucken soll :cyclops:

nahpets 4. Sep 2015 15:11

AW: FireDAC : Trigger geht nicht
 
Was steht bitte in der Create-Anweisung des Triggers:
Delphi-Quellcode:
CREATE TRIGGER trigger-name for table-name
   [ACTIVE | INACTIVE]
   {BEFORE | AFTER} <actions>
   [POSITION number]
   AS
Before oder After?
Kannst Du im Trigger irgendeine (wie auch immer geartete) Ausgabe machen, um zu erkennen, ob der Trigger beim Aufruf aus dem Programm überhaupt aktiv wird?
Er scheint ja zuweilen zu arbeiten, oder konntest Du schon sicherstellen (dich vergewissern), dass er auf jeden Fall immer aufgerufen wird.

Oder:

Könntest Du ein (vorübergehendes) Logging in den Trigger einbauen, analog zu http://www.firebirdsql.org/refdocs/l...exception.html

Eigentlich erscheint mir der Trigger korrekt zu sein, er dürfte ja diesem Beispiel entsprechen:
Delphi-Quellcode:
create trigger biu_parts for parts
  before insert or update
as
begin
  /* conditional code when inserting: */
  if (inserting and new.id is null)
    then new.id = gen_id(gen_partrec_id, 1);
 
  /* common code: */
  new.partname_upper = upper(new.partname);
end
Wenn Du im Trigger eine Exception schmeißt (ohne jede Bedingung, immer und grundsätzlich
Delphi-Quellcode:
EXCEPTION [<exception-name> [custom-message]]
, bekommst Du sie aus dem Programm zu sehen? Wenn nein, dann wird der Trigger (höchstwahrscheinlich) nicht immer aufgerufen.
Allerdings hab' ich überhaupt keine Idee, warum es eine derartige Situation geben sollte.

Uwe Raabe 4. Sep 2015 15:17

AW: FireDAC : Trigger geht nicht
 
Zitat:

Zitat von Hansa (Beitrag 1314757)
Das dürfte alles nicht viel sein, irgendeine dumme Einstellung, aber momentan ein Rätsel. Mir fällt langsam nichts mehr ein, wo ich nachgucken soll :cyclops:

Wie schon erwähnt, prüfe doch ob das ID-Feld wirklich als TFDAutoIncField erkannt wurde.

Dann das obligatorische "ist der Stecker drin?", sprich: bist du auch sicher mit derselben Datenbank verbunden?

Wenn es machbar ist, lannst du ja auch mal die nötigen Sourcen hier anhängen, damit wir auch was sehen können. :glaskugel:

Daniel 4. Sep 2015 15:19

AW: FireDAC : Trigger geht nicht
 
FireDAC erkennt das ID-Feld als Primärschlüssel und fordert daher, dass es einen Wert erhält. Dieser wird aber erst auf dem Server (!) durch Deinen Trigger zugewiesen. Man muss FireDAC also dazu bringen, ein Feld, welches als "required" markiert ist, leer zum Server zu senden. Das geht entweder, indem man es als "AutoInc"-Feld deklariert (siehe Uwe) oder aber, indem man in den Update-Options die Eigenschaft "CheckRequired" auf FALSE setzt.

Bei Deinem Code kommt es gar nicht soweit, dass der Trigger angestossen werden könnte, weil FireDAC den Post-Request als unvollständig betrachtet und ablehnt. Die Daten erreichen den Server nicht einmal.

Hansa 4. Sep 2015 16:19

AW: FireDAC : Trigger geht nicht
 
@nahpets : das mit der Exception (für mkinzler : Datenbank-Exception :mrgreen: ) hatte ich schon gemacht und deshalb gesehen, dass der Trigger tatsächlich nicht zuschlägt (natürlich für Insert ein BI-Trigger).

Und die Ursache für den Effekt ist tatsächlich genau das, was Uwe und Daniel angemerkt haben. Ich habe FireDAC eben nicht mitgeteilt, dass die ID ein über Trigger automatisch aktualisiertes Feld ist. Ich habe das jetzt nachgeholt und der ursprüngliche Fehler ist weg. Das haben sie aber trotz der Wichtigkeit unnötigerweise gut in OI-Untermenü versteckt bei Update-Options.

Jetzt gibt es aber noch ein ähnliches Phänomen :
Zitat:

violation on primary or unique key...Problematic Key is ("NR" = 101
Die Tabelle besteht ja nur aus ID,NR,BEZ, wobei NR als unique deklariert ist. Was ist das nun wieder ? Die Tabelle ist momentan leer.

hoika 4. Sep 2015 16:19

AW: FireDAC : Trigger geht nicht
 
Hallo,
vielleicht hilft das ja.

http://docwiki.embarcadero.com/RADSt..._%28FireDAC%29

Also:
Set TField.AutoGenerateValue to arAutoInc for an auto-incrementing field. This method does not create a TFDAutoIncField, it works with any field type, and may require additional field properties setup, like setting ProviderFlags, Required and ReadOnly.


Zu spät ... (AutoInc)
So, jetzt zeig mal deine Tabellenstruktur.


Heiko

Hansa 4. Sep 2015 16:29

AW: FireDAC : Trigger geht nicht
 
Tabelle ? Im Prinzip steht die oben schon, egal.

Code:
CREATE TABLE WG8 (
    ID              INTEGER NOT NULL,
    NR              INTEGER NOT NULL,
    BEZ             VARCHAR(25) DEFAULT '' NOT NULL
);
ALTER TABLE WG8 ADD CONSTRAINT UNQ_WG8 UNIQUE (NR);
ALTER TABLE WG8 ADD CONSTRAINT PK_WG8 PRIMARY KEY (ID);

Frank Ingermann 4. Sep 2015 21:00

AW: FireDAC : Trigger geht nicht
 
Hallo zusammen,

ich hoffe, ich darf hier mal kurz "reingrätschen" ;-) :

In dem Trigger steht:

IF (NEW.ID IS NULL) THEN
NEW.ID = GEN_ID(GEN_TEST_ID, 1);

...klingt im Prinzip gut, geht aber schief wenn durch irgendwas (z.B. FireDAC) die ID
schon mit 0 (also numerisch 0, nicht dem Zustand NULL) vorbelegt wurde.

besser:

IF ((NEW.ID IS NULL) or (NEW.ID=0)) THEN
NEW.ID = GEN_ID(GEN_TEST_ID, 1);

oder auch (macht das selbe, nur etwas kürzer):

IF (coalesce(NEW.ID,0) = 0) THEN // coalesce: falls NULL, nimm' das zweite Argument
NEW.ID = GEN_ID(GEN_TEST_ID, 1);

MMn: Entweder der Trigger in der DB feuert gar nicht (weil durch irgendwas in FireDAC
das INSERT gar nicht erst bis zum Server durchkommt), oder er feuert zwar, macht
dann aber nichts, weil die ID nicht NULL sondern 0 ist.

lg, Frank

P.S. gibt es hier so etwas wie einen Thread zur Vorstellung neuer User?
("Neu" bin ich zwar nicht mehr, aber zumindest hier gerade neu... :-D )

Perlsau 4. Sep 2015 21:42

AW: FireDAC : Trigger geht nicht
 
Soweit ich informiert bin, haben Trigger in der DB Vorrang vor Client-Triggern. Im konkreten Fall hätte dann der Trigger, der die Id-Erzeugung im Generator anstößt, Vorrang vor dem im Firedac-Query eingestellten Defaultwert. In FireDac gebe ich an, welcher Generator für die Erzeugung des Primary-Key (den ich ebenfalls angebe) zuständig ist. FireDac führt das aber nur aus, wenn in der DB nicht bereits ein Trigger das Setzen dieses Wertes anstößt.


Zitat:

Zitat von Frank Ingermann (Beitrag 1314807)
P.S. gibt es hier so etwas wie einen Thread zur Vorstellung neuer User? ("Neu" bin ich zwar nicht mehr, aber zumindest hier gerade neu... :-D)

Nicht daß ich wüßte ... aber ein Sei Willkommen! kann ja nicht schaden :-D

Hansa 4. Sep 2015 21:52

AW: FireDAC : Trigger geht nicht
 
Zitat:

Zitat von Frank Ingermann (Beitrag 1314807)

...klingt im Prinzip gut, geht aber schief wenn durch irgendwas (z.B. FireDAC) die ID
schon mit 0 (also numerisch 0, nicht dem Zustand NULL) vorbelegt wurde.

besser:

MMn: Entweder der Trigger in der DB feuert gar nicht (weil durch irgendwas in FireDAC
das INSERT gar nicht erst bis zum Server durchkommt), oder er feuert zwar, macht
dann aber nichts, weil die ID nicht NULL sondern 0 ist.

1. reingrätschen kann ja nichts schaden, ja, was macht Firedac da ? Werden ohne zutun etwa Table-Felder automatisch bestzt ? Aber :
2. das mit der ID ist schon geklärt, der Trigger scheint schon zu feuern. In externem IBExpert (bereits ca. 5mal gesagt) macht er das ja sowieso, aber auch in anderem eigenem Programm (allerdings mit FibPlus als Zugriffskomponenten)
3. Die Frage dreht sich nur noch darum, warum jetzt plötzlich der unique Key Ärger macht. Muss man das eventuell ähnlich einstellen, wie mit dem AutoInc-Feld ? Oder hängt das gar zusammen ? Ich sehe aber da nichts in FireDac, was irgendwie in diese Richtung gehen könnte.
4. Wie gesagt : bei leerer Tabelle schlägt der Insert fehl mit NR, die es noch gar nicht gibt. Und NR hat mit der ID nichts zu tun. Ist halt lediglich unique, was mein Programm besser sowieso der Datenbank im Fehlerfalle überlassen sollte.

@Perlsau : Davon ging ich auch aus. Der DB muss aber offensichtlich das als AutoInc zu behandelnde Feld mitgeteilt werden. Die Frage lautet jetzt also, was FireDac da weshalb genau macht. Warum interessiert sich FireDac überhaupt um einen unique Key ? Trigger/ID ist vorerst erledigt ! Also nochmals : der Trigger geht definitiv richtig ! Und der nicht vorhandene unique landet nicht in der DB wegen
Code:
Problematic Key is ("NR" = 101
Die Nr. 101 ist doch gar nicht da !

jobo 4. Sep 2015 22:23

AW: FireDAC : Trigger geht nicht
 
geth die Meldung noch weiter? Ich seh da "(" auf nicht zu. Ist die Nummer 101 vollständig oder ist da was abgeschnitten?
Mit welcher Aktion erzeugst Du diesen Fehler? Offenbar ja mittels Delphiprogramm.
Welche Werte stehen vor dem Post im Record? Ist "Nr" belegt oder leer?
Leer sollte es nicht sein, da es not null definiert ist.

nahpets 4. Sep 2015 22:33

AW: FireDAC : Trigger geht nicht
 
Sicher, dass die 101 noch nicht da ist?

Ausgehend von den Quelltextfragmenten, kann es die 101 nur einmal geben.

Zumindest gehe ich mal davon aus, dass du da eine For-Schleife mit sowas wie
Delphi-Quellcode:
for i := 0 to n
hast. Wenn dem so ist, warum wird dann nicht schon bei Nr. 100 gemoppert?

Wenn i bei 1 beginnt, hieße das aber, dass bereits der erste Satz als Schlüsselverletzung angesehen wird. Dies erscheint mir dann aber doch ziemlich schräg, könnte aber bedeuten, dass irgend etwas konfigurationstechnisch dafür sorgt, dass jeder Datensatz mindestens zweimal (aus Sicht der Datenbank) dort ankommt.

Was passiert, wenn Du (vorerst) nach jedem Satz mal ein Commit machst? Quasi
Delphi-Quellcode:
if i mod 1 = 0 then begin
Zumindest müsstest Du bei einem satzweisen Commit ja dann nachschauen können, ob schon 'ne 101 da ist. Wenn nicht, dann muss das Problem bei FireDac liegen.

Eventuell könntest Du den Quelltext mal ein bisserl umbauen:
Delphi-Quellcode:
DM.DS.Insert;
DM.DS.FieldByName('NR').AsInteger := i + 100;
DM.DS.FieldByName('BEZ').AsInteger := i;
try
  DM.DS.Post;
except
  on e : Exception do begin
    DM.DS.Cancel;
    mem1.Lines.Add(Format('i = %d - %s'[i,e.Message]));
  end;
end;
Wie oft bekommst Du hier i = 1 zu sehen? Denn das müsste ja eigentlich zu dem Fehler führen.
Zugegeben sind mein Vorschläge gerade nur reines stochern im Nebel, darfst sie von daher gerne ignorieren ;-)

Frank Ingermann 4. Sep 2015 22:38

AW: FireDAC : Trigger geht nicht
 
Hallo zusammen,

@Hansa: ich bin da "gebranntes Kind": wenn <irgendwas> mit IBExpert geht,
dann heißt das noch lange nicht, dass es mit Firebird per se geht...
(sorry, Holger... ;-)

"Der Trigger scheint zu feuern"...
Vergiss' das. Lege einen neuen Generator an, der NUR im Trigger erhöht wird.

Wenn der nach deinem Test erhöht wurde, dann wurde der Trigger gefeuert.
Sonst nicht. ("Generators run outside of transaction control!")

(Ob die Transaktion danach dann vlt. einem Rollback zum Opfer gefallen ist,
weisst Du dann aber immer noch nicht ;) )

Die Sache mit dem Unique Key ist noch mal eine ganz andere Baustelle.

Ohne das Umfeld genauer zu kennen, kann ich nur mutmaßen: wenn die DB
meckert, daß es zum Zeitpunkt des Inserts schon einen Datensatz mit NR=<x>
gab, dann wird das wohl so gewesen sein... ;)

Vlt. noch eine andere Instanz des Programms? Nicht-COMMITtete Transaktion?

Die Transaktionen in Fb funktionieren, dafür lege ich meine Hand in's Feuer(!).
Die Generatoren/Sequenzen auch. Wenn im Zusammenspiel zwischen Programm und
Fb-Server etwas nicht funktioniert, liegt das Problem erfahrungsgemäß
irgendwo in der Mitte ;)

lg, Frank

Hansa 4. Sep 2015 23:00

AW: FireDAC : Trigger geht nicht
 
Zitat:

Zitat von nahpets (Beitrag 1314814)
Sicher, dass die 101 noch nicht da ist?

Ausgehend von den Quelltextfragmenten, kann es die 101 nur einmal geben.

Es gibt sie nicht mal einmal. Das 1. Insert scheitert schon. Siehe Fehlermeldung. Ich versuche also, einen nicht vorhandenen Datensatz mit eingebautem unique Key in die DB einmalig einzufügen und das 1-mal und dann so was. Das i sollte schon hochgezählt werden, aber es besteht nur noch Folgendes :
Delphi-Quellcode:
i := 0;
Danach ist alles auskommentiert, was eigentlich geplant war. Jetzt kommt noch der Witz : Habe das Unique-Key-Feld Nicht-unique gemacht und der Fehler ist nicht weg ! Ok, Datenbank fast leer gemacht und immer noch dasselbe. Näcgster Ansatz wäre dann die DB an sich. Gibt ConnectionName da vielleicht falsche Namen zurück ? Die lasse ich mir nämlich vorsichtshalber anzeigen. Und sie beziehen sich auch auf die zu bearbeitende DB.

Wäre ja naheliegend, dass ich die falsche benutze. Sehe das aber nicht so.

nahpets 4. Sep 2015 23:35

AW: FireDAC : Trigger geht nicht
 
Moment, nochmal zum Mitschreiben. Du machst also nur noch das:
Delphi-Quellcode:
i := 0;
DM.DS.Insert;
DM.DS.FieldByName('NR').AsInteger := i + 100;
DM.DS.FieldByName('BEZ').AsInteger := i;
DM.DS.Post;
und das scheitert beim ersten Datensatz in eine leere Tabelle mit einer Schlüsselverletzung im Feld Nr in einer Tabelle, die für das Feld Nr keinen eindeutigen Schlüssel hat?

1. Drop Table und neumachen.
2. Erneut probieren, Fehler weg?
3. Wenn nein, neue Datenbank machen, nur diese Tabelle und den Trigger und erneut probieren.
4. Fehler weiter da? Rechner ausschalten und morgen erneut probieren ;-) Nein, ehrlich gesagt keine Ahnung.

Gibt es irgendeinen Cache, der Daten vorhält..., so dass das Programm irgend etwas "aus der Vergangenheit" sehen könnte, was in der Realität nicht mehr existiert?
Aber dann dürfte ja die Datenbank nicht meckern. Gibts da 'nen Clone von, auf den Du noch (irrtümlich) zugreifst?

Entwicklungsumgebung mal zugemacht?
Datenbank neu gestartet?
Neues, leeres Projekt mit nur dem Datenbankzugriff und dem Insert in diese Tabelle (nicht per Copy&Paste aus dem "alten" Projekt übernehmen, wer weiß, was jetzt da nicht stimmt)?
Kenne FireDac nicht (mein Delphi ist zu alt), gibt es denn da die Möglichkeit etwas so stark zu "verkonfigurieren", dass daraus ein derartiges Problem resultieren könnte?

Glaube nicht, dass ich hierzu noch irgendwas halbwegs sinnvolles schreiben könnte :-(

Perlsau 5. Sep 2015 00:24

AW: FireDAC : Trigger geht nicht
 
Hansa, ich seh da nur das eine Problem aus deinem Eingangsposting:

Zitat:

Zitat von Hansa (Beitrag 1314732)
Delphi-Quellcode:
    DM.DS.Insert;
    DM.DS.FieldByName('NR').AsInteger := i + 100;
    DM.DS.FieldByName('BEZ').AsInteger := i;
    DM.DS.Post;
    if i mod 1000 = 0 then begin
mem1.Lines.Add(IntToStr (i));
      DM.Trans.Commit;
      DM.Trans.StartTransaction;
    end;

Du versuchst, ein Feld, das durch eine Trigger-Generator-Kombination von der DB beim Insert automatisch befüllt wird, im vom Clienten aus zuzuweisen. Also ent oder weder ...

In solchen Fällen erhalte ich ebenfalls eine Fehlermeldung, das kann nicht funktionieren. Du legst ja auch den Wert des PrimaryKeys nicht im Client fest, wenn du dafür in der DB ebenfalls einen Trigger hast, der bereits reagiert, bevor die vom Client gesendeten Daten eingetragen werden. Laß also einfach die Zuweisung an das Feld NR im Client weg und du hast dieses Problem nicht mehr.

hoika 5. Sep 2015 05:17

AW: FireDAC : Trigger geht nicht
 
Hallo,
das Autoinc-Feld ist aber ID, nicht NR ...


Heiko

haentschman 5. Sep 2015 05:36

AW: FireDAC : Trigger geht nicht
 
Hallo Hansa,

mache doch einfach mal ein
Code:
FireDacQuery.SQL.Text:= 'insert into TABLE (NR,BEZ) values (:nr,:be)'; // vor der Schleife -> nur einmal zuweisen. Macht bei den heutigen Rechnern kaum einen Unterschied.
FireDacQuery.ParamByName('nr').AsInteger:= i + 100; //die Parametersyntax könnte ggf. abweichen
FireDacQuery.ParamByName('be').AsInteger:= i;
FireDacQuery.ExecSQL;
Wie schon gefragt. (Antwort vieleicht überlesen) Wird der Generator erhöht wenn dein Fehler kommt?

nahpets 5. Sep 2015 07:23

AW: FireDAC : Trigger geht nicht
 
Zitat:

Zitat von haentschman (Beitrag 1314822)
Hallo Hansa,

mache doch einfach mal ein
Code:
FireDacQuery.SQL.Text:= 'insert into TABLE (NR,BEZ) values (:nr,:be)'; // vor der Schleife -> nur einmal zuweisen. Macht bei den heutigen Rechnern kaum einen Unterschied.
FireDacQuery.ParamByName('nr').AsInteger:= i + 100; //die Parametersyntax könnte ggf. abweichen
FireDacQuery.ParamByName('be').AsInteger:= i;
FireDacQuery.ExecSQL;
Wie schon gefragt. (Antwort vieleicht überlesen) Wird der Generator erhöht wenn dein Fehler kommt?

Hansa schrieb weiter oben, dass der Trigger funktioniert.

Aber ließe sich der obige Code nicht noch erweitern, in dem man immer eher sowas macht:
Delphi-Quellcode:
sSQL := 'insert into TABLE (NR,BEZ) values (%d,%s);';
DM.Query.SQL.Clear;
DM.Trans.StartTransaction;
for i := 1 to n do begin
  DM.Query.SQL.Add(Format(sSQL,[i + 100,QuotedStr(i)]));
  if i Mod 1000 = 0 then begin
    DM.Query.ExecSQL;
    DM.Trans.Commit;
    mem1.Lines.Add(IntToStr(i));
    DM.Query.SQL.Clear;
    DM.Trans.StartTransaction;
  end;
end;
Hier werden dann halt immer 1000 Inserts an einem Stück an die Datenbank gegeben und von ihr verarbeitet, statt satzweise. Hab' sowas in de Art vor Jahren mal bei 'ner SQL-Server-Datenbank gemacht, dass hat bei der zu verarbeitenden Datenmenge etliche Stunden Zeitersparnis gebracht.

Daniel 5. Sep 2015 08:29

AW: FireDAC : Trigger geht nicht
 
Zitat:

Zitat von nahpets (Beitrag 1314823)
Hier werden dann halt immer 1000 Inserts an einem Stück an die Datenbank gegeben und von ihr verarbeitet, statt satzweise. Hab' sowas in de Art vor Jahren mal bei 'ner SQL-Server-Datenbank gemacht, dass hat bei der zu verarbeitenden Datenmenge etliche Stunden Zeitersparnis gebracht.

Diesen Ansatz gibt es in FireDAC nativ und je nach DBMS kann FireDAC gegenüber eine Lösung mit Format() optimieren. Nennt sich Array DML.
(http://docwiki.embarcadero.com/RADSt...ehls_(FireDAC))

nahpets 5. Sep 2015 08:33

AW: FireDAC : Trigger geht nicht
 
@Daniel
Das ist doch eine absolut elegante Lösung.

IBExpert 5. Sep 2015 08:49

AW: FireDAC : Trigger geht nicht
 
mal zurück zur eigentlichen Problematik

Datasets, die für Delphi und Firebird entwickelt wurden, wie zum Beispiel IBObjects, haben Properties, mit denen man den verbundenen Generator festlegen kann. Das heisst dann bei IBO z.B. KeyGeneratorLink o.ä.

Da man in Firebird selbst entscheiden kann, welcher Generator pro Tabelle benutzt wird, ist dieser Weg sehr praktisch, denn bei mir gibt es für alle Tabellen immer nur einen Generator, aus dem alle ID Werte geholt werden.

Nun kommen aber mehr generalisierte Komponenten wie Firedac nicht mit jeder unterstützten Plattform zu 100% klar. Mein Tip: Verliert euch nicht in der Abhängigkeit zu einer Komponente. Wie oben schon erwähnt bekommt Firebird noch gar nicht mit, das es einen Insert geben sollte, wenn auf Ebene der Delphi Komponente Bedingungen noch gar nicht erfüllt sind. Man könnte das als Mangel der FireDac Komponenten im Zusammenspiel mit Firebird betrachten oder einfach mal in den Dataset Events nach OnNewRecord suchen.

Wenn Ihr im OnNewRecord Ereignis dann ein SQL startet "select gen_id(genid,1) from rdb$database" und den Rückgabewert in euer Primärschlüsselfeld übertragt, werdet Ihr feststellen, das es gar nicht so schwer ist, selbst die Kontrolle über solche Vorgänge zu haben. Wenn Ihr ein Generator pro Tabelle habt, könntet ihr das auch dynamisch machen, weil im Event ja der Sender(also das Dataset) kommt und das ja hoffentlich properties hat, aus denen sich der Tabellenname ergibt.

Und wenn Ihr das Event nicht für jedes Dataset manuell neu setzen wollt, könnt Ihr mit sehr wenig Aufwand auch ein global benutzbares Event dafür im Datenmodul generieren und das ggf. jedem Dataset in der components List im OnCreate zuweisen. Oder noch viel cleverer, einfach eine Ableitung der benutzten Dataset Klasse machen, in dem das automatisch passiert.

Wichtig: Nur weil Firebird und FireDAC vorne vier gleiche Buchstaben haben, ist FireDAC keineswegs die erste Wahl für Firebird und Delphi.

IBExpert 5. Sep 2015 08:59

AW: FireDAC : Trigger geht nicht
 
Zitat:

Zitat von nahpets (Beitrag 1314823)
Hier werden dann halt immer 1000 Inserts an einem Stück an die Datenbank gegeben und von ihr verarbeitet, statt satzweise. Hab' sowas in de Art vor Jahren mal bei 'ner SQL-Server-Datenbank gemacht, dass hat bei der zu verarbeitenden Datenmenge etliche Stunden Zeitersparnis gebracht.

Wenn es um maximum Speed mit Firebird geht, sammel die Daten als Insert Befehle zeilenweise mit Semikolon getrennt z.B. lokal in einer TStringlist (aber nicht mehr als 255 Befehle und nicht länger als 32kb) und übergebe die Werte direkt im Text (keine parameter benutzen). Danach dann noch eine Zeile mit "execute block as begin " davor und ein "end" an Ende dahinter und schick das in eine Query.sql property und führ das aus mit Query.execSql.

Damit sind auch aus Delphi heraus bis zu 10000 Inserts pro Sekunde in die Firebird DB machbar.

Hansa 5. Sep 2015 12:35

AW: FireDAC : Trigger geht nicht
 
Zitat:

Zitat von IBExpert (Beitrag 1314826)
Wichtig: Nur weil Firebird und FireDAC vorne vier gleiche Buchstaben haben, ist FireDAC keineswegs die erste Wahl für Firebird und Delphi.

Ist wohl so und ich habe das vorher auch schon geahnt. Denn : wenn EINE Komponentensammlung mehrere Datenbanken unterstützt dann kann sie meiner Meinung nach nie für eine spezielle, hier also Firebird, optimal sein.

Aber egal. FireDac beschwert sich immer noch beim Einfügen eines unique Key-Felds. Offensichtlich wird immer nur auf den Titel geantwortet und nicht im Kontext. :shock: Ich werde das Problem deshalb jetzt reduzieren. Neue Datenbank anlegen mit einer Table und dann mal sehen. Da sich dadurch wohl nichts ändern wird, werde ich gegebenenfalls neues Thema aufmachen. Dürfte nicht lange dauern. :mrgreen:

Wenns endlich geht, befülle ich die DB mit FireDac dann mal neu. Das Daniel-Array und die Überlegungen von Holger Klemt werde ich dann eventuell auch einbauen. Mein Generator (es gibt nur einen) steht bei ca. 2.000.000. 10.000 Einfügeoperationen pro Sek. wär natürlich schon gut.

Frank Ingermann 5. Sep 2015 12:51

AW: FireDAC : Trigger geht nicht
 
Hallo Hansa,

wie schon gesagt: wenn Firebird meckert, dass es die Nr. 101 schon gibt, dann ist das so(!).

Wenn die Tabelle beim Start wirklich leer ist, dann kann das eig. nur daran liegen, dass
zwei Insert-Befehle mit der gleichen NR nacheinander abgesetzt werden. Am besten mal
in die MON$-Tabellen im Firebird schauen oder mit einem Trace-Tool (z.B. Thomas
Steinmaurer's Fb Tracemanager) nachsehen, was *wirklich* im Server ankommt.

Nur so kannst Du irgendwelche "behind the scene magic" in FireDAC ausschliessen.


Hi Holger,

( lange nicht gesehen :wink: )

ich stimme Dir in allen Punkten zu. Nur noch ein Hinweis dazu:

Wenn man mit der EXECUTE BLOCK-Methode arbeitet, sollte man trotzdem jeden Generatorwert per
"select gen_id..." aus der DB holen. Es kamen schon mal Leute auf die Idee, die IDs Client-seitig
hochzuzählen und anschließend mit SET GENERATOR den Generator in der DB auf den zuletzt
vergebenen Wert zu setzen, oder sich mit "select gen_id(genid,1000) from rdb$database"
mal eben 1000 Generatorwerte auf einen Schlag zu "reservieren".

Beides funktiniert aber nur, wenn man exklusiven Zugriff auf die DB hat - sobald mehrere
User oder Programminstanzen an der DB hängen, die das u.U. gleichzeitig machen können,
kann das furchtbar schiefgehen... immer dran denken, dass Generatoren außerhalb jeder
Transaktions-Kontrolle arbeiten! :thumb:

lg, Frank

IBExpert 5. Sep 2015 13:38

AW: FireDAC : Trigger geht nicht
 
Moin, Frank,

jo, volle Zustimmung, aber in der ID Spalte brauchst du ja auf dem Weg über execute block gar nichts eintragen und kannst die einfach weglassen, denn da greift ja ggf. der auf der Tabelle definierte Autoinc Trigger und eine Zugriffskomponente brauch sich da auch nicht einmischen.

Von einem früheren Projekt, wo der Kunde mit Informix gearbeitet hat, weiss ich noch von einem Bug, der erstmalig nach ca 9 Monaten Echtbetrieb aufgetaucht ist. Die Primärschlüssel und damit auch die Fremdschlüssel für Details wurden immer aus Nummernkreistabellen geholt. Dabei nutzte man ein Verfahren, was angeblich auch im Multiuserbetrieb mit Informix sicher war.

Dummerweise ist da nach 9 Monaten mal jemandem in der Logistik ein Lieferschein aufgefallen, auf dem keine Positionen waren. Der Mitarbeiter hat den also so abgearbeitet und eben nichts geliefert, den Lieferschein aber erst abends seinem Vorgesetzten vorgelegt. Dieser hat dann am Folgetag in der Fachabteilung nachgefragt, weil laut Auftragsbearbeitung sollte da auch was geliefert werden.

Ein wenig Recherche und schon stellte man fest, das die Positionen dummerweise auf einen anderen Lieferschein gebucht wurden, der für einen anderen Kunden war und schon ausgeliefert wurde. Auf Nachfrage bei dem Kunden hatte der sich dann auch schon gewundert, was da geliefert wurde, weil er dazu wiederum auch keine Bestellungen finden konnte.

Der Vorgang Lieferschein anlegen war ein Modul in der im Haus selbst programmierten Software und dort wurde dann einfach mal der Wert aus der Tabelle mit den Nummern ausgelesen, aber beim schreiben der Erhöhung um 1 war da der allseits beliebte try ... except {mir doch egal} end; drumherum, also im Falle einer Exception wird nix gemacht.

Da sich also wohl erstmals nach 9 Monaten Echtbetrieb zwei Buchungen in die Quere kamen, rächte sich diese Schlampigkeit. Da auch noch die Lieferscheinnummer als Fremschlüssel genommen wurde (und nicht ein rein technischer Wert wie zum Beispiel die von allen Firebird Programmierern bevorzugten Generatorwerte, die mit 64 Bit reichlich Platz bieten), wurden daher wohl die Detailbuchungen vom Client dem falschen Kopf zugeordnet. Oder mit anderen Worten, diverse Paletten wurden kreuz und quer durch die Gegend sinnlos hin- und zurück gefahren, weil ein Programmierer da ein ungeeignetes Verfahren gewählt hat. Im Nachhinein ist dann wohl jemandem noch aufgefallen, das wohl schon häufiger vorkam, aber wohl bisher noch nie ein komplett leerer Lieferschein erzeugt wurde.

Wie ich und Frank und viele andere sicher auch bestätigen können, ist der Weg über den Trigger und Gen_id() bzw. direkt über Gen_id() der einzig richtige Weg für Firebird, um netzwerksicher und transaktionssicher bei der Primärschlüsselerzeugung zu sein. Ich für meinen Teil benutz dabei dann auch noch einen einzigen Generator für alle Tabellen, was diverse weitere Vorteile hat.

Da es diese Konstrukte aber bei anderen Datenbanksystemen nicht gibt, muss eine Komponentenbliothek wie FireDAC dann eben auch unter Berücksichtigung der TDataset Kompatibilität da diverse Kompromisse und Workarounds machen.

Ich für meinen Teil arbeite mit TDatasets im Insert/Edit/append/post Verfahren schon seit 10 bis 15 Jahren nicht mehr freiwillig (ist aber von der Höhe des Honorars abhängig), weil ich der Datenbank in SQL Sprache sage, was ich von der will und Datasets nur zum Lesen für select Ergebnisse benutze.

Daniel 5. Sep 2015 14:04

AW: FireDAC : Trigger geht nicht
 
Moin,

was FireDAC da macht, ist weder ein Kompromiss noch ein Workaround. Entweder verbindest Du das PK-Feld mit dem Generator und hast den Wert dann schon direkt nach dem Append/Insert, sofern Du ihn da benötigst oder Du stellst die Komponente so ein, dass Du das PK-Feld leer absenden kannst.
In jedem Fall wird der Entwickler gezwungen, sich mit der Frage zu befassen, damit z.B. solche wie von Holger geschilderten Fehler nicht passieren.

haentschman 5. Sep 2015 15:05

AW: FireDAC : Trigger geht nicht
 
Hallo Hansa...:roll:

probiere doch einfach mal die einfache Query. Da wirst du sehen das das funktioniert. Das Problem liegt zu 99.99% wie schon mehrfach gesagt an den Einstellungen der Table die du verwendest und nicht am eigentlichen Trigger der DB. Für das Layer 8 Problem gleich die Komponenten madig zu machen ist nicht nett. :P

Hansa 6. Sep 2015 14:19

AW: FireDAC : Trigger geht nicht
 
Moin,

ich habe doch nicht rumgemosert. Lediglich angemerkt, dass manche Sachen nicht da sind, wo man sie erwarten würde und vom Namen her auch der Zweck nicht direkt erkennbar ist. Deshalb ist manches schlecht zu finden. Nachdem Dank Daniel klar war, warum die ID Ärger macht, dann aber ging, hatte ich mir nochmals die Update-Options angesehen, weil der unique Key ja auch anfangs nicht ging. Und siehe da, da gibts KeyFields. Was das bei UpdateOptions zu suchen hat, obwohl das ja auch nur zum Lesen gebraucht wird, führt einen in die Irre. Immerhin : das Wort Key ist auch im "unique Key" zu finden. Da habe ich dann das Feld NR eingetragen und dann ging auch das.

Ich habe also grob geschätzt ca. 6 St. gebraucht für :

Komplette neue DB anlegen, samt Triggern und Keys mit IBExpert. Da mir ja nicht geglaubt wurde, dass der Trigger auf DB Seite geht bzw. das einfach überlesen/ignoriert wurde, habe ich unnötig viel Zeit in die Überprüfung des Triggers investiert.

In Delphi habe ich dann noch Datamodul mit dem FireDAC Zeugs angelegt und das alles soweit richtig eingestellt. Viel lesen in der Hilfe kam noch dazu und jetzt kommt noch noch etwas zum meckern :

Aber eher an Emba : nicht nur bei den Update Options sagt mir die Hilfe folgendes :

Code:
Embarcadero Technologies verfügt zurzeit über keine zusätzlichen Informationen. Bitte unterstützen Sie uns bei der Dokumentation dieses Themas, indem Sie Ihre Kommentare auf der Diskussionsseite eingeben.
Man muss aber mehr fragen/suchen als unbedingt nötig.

Das eigtliche Delphi-Testprogramm habe ich ja auch noch gemacht und getestet. Jedenfalls bin ich mit sechs St. Aufwand zufrieden. Jemand, der sich weniger auskennt, der hätte wohl eher 6 Tage gebraucht und ein blutiger Anfänger sogar 6 Wochen.

mkinzler 6. Sep 2015 14:25

AW: FireDAC : Trigger geht nicht
 
Und hättest Du jetzt noch den Debugger und den SQL-Monitor verwendet ...

Hansa 6. Sep 2015 15:06

AW: FireDAC : Trigger geht nicht
 
Für FireDAC oder was ?

mkinzler 6. Sep 2015 15:06

AW: FireDAC : Trigger geht nicht
 
Ja.

Daniel 6. Sep 2015 18:05

AW: FireDAC : Trigger geht nicht
 
Ab und an muss man ein wenig klicken, aber hier sind die Optionen allesamt aufgelistet:
http://docwiki.embarcadero.com/Libra...ons_Properties


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:01 Uhr.
Seite 1 von 2  1 2      

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