![]() |
Datenbank: Firebird • Version: 5.0 • Zugriff über: FireDAC
Problem mit Generator
Hallo,
in einer Anwendung verwende ich FireDAC mit Firebird 5.0. Einige Tabellen haben einen generator zur erzeugung des eindeutigen Primärschlüssels und in der Regel funktioniert das auch. Ich habe für das Programm Update auch einen Mechanismus geschrieben der mit der alten Version der Software zuerst ein transparentes Backup anlegt. Die neue Version der Software findet das und stellt es unter einem anderen Dateinamen wieder her. Danach werden die Inhalte aus dieser Datei mittels FDBatchMove in die von der neuen Programmversion mitgelieferte Datenbankdatei übernommen. Und es werden die Werte der Generatoren übernommen. Nun habe ich festgestellt, dass es danach für den jeweils ersten Versuch einen neuen Satz zu einer der Tabellen die einen generator benutzen hinzuzufügen mit einer Verletzung des Primärschlüssels knallt: "[FireDAC][Phys][FB]violation of PRIMARY or UNIQUE_KEY constraint "INTEG_104" on table "MY_FAILURELIST". Problematic key value is ("FAILURE_ID" = 227). Schaue ich mir die DB vor dem ersten Schreibzugriff an stelle ich fest, dass der Wert des betreffenden Generators auf 227 steht. Schaue ich mir die Tabelle an gibt es auch schon einen Eintrag mit 227 als Schlüsselwert. Woher kommt das? Bei Späteren Schreibzugriffen werden scheinbar problemfrei Datensätze erzeugt... Der Trigger in der DB ist so definiert:
Code:
Hier der Code der in diese Tabelle schreibt:
CREATE TRIGGER BI_KASSE_TSE_FAILURE_ID FOR KASSE_TSE_FAILURES
ACTIVE BEFORE INSERT POSITION 0 AS BEGIN IF (NEW.FAILURE_ID IS NULL) THEN NEW.FAILURE_ID = GEN_ID(KASSE_FAILURE_ID_GEN, 1); END;
Delphi-Quellcode:
function TFailureLog.AddEntry(FailureType : TFailureType;
FailureCode : Int32; const FailureText : string): Integer; var FailureTypeStr : string; begin Result := -1; if not self.FSuppressLogging then begin try FailureTypeStr := ConvertFailureTypeToString(FailureType); // Relevant, damit FireDAC beim Append später nicht wegen dem noch leeren // Primärschlüssel meckert. FQuerySave.UpdateOptions.AutoIncFields := 'FAILURE_ID'; FQuerySave.SQL.Text := 'select FAILURE_ID, DEVICE_ID, FAILURE_TYPE, ' + 'FAILURE_CODE, FAILURE_TEXT, FAILURE_REMARK ' + 'from KASSE_TSE_FAILURES'; FQuerySave.Open; FQuerySave.Append; FQuerySave.FieldByName('DEVICE_ID').AsInteger := FCurrentDEVICEID; FQuerySave.FieldByName('FAILURE_TYPE').AsString := FailureTypeStr; FQuerySave.FieldByName('FAILURE_CODE').AsInteger := FailureCode; FQuerySave.FieldByName('FAILURE_TEXT').AsString := FailureText; FQuerySave.FieldByName('FAILURE_REMARK').AsString := ''; FQuerySave.Post; Result := FQuerySave.FieldByName('FAILURE_ID').AsInteger; FQuerySave.Close; except On e:Exception do log.SendException(cCategory, 'Fehler beim Speichern eines Fehlerlog '+ 'Eintrags: ' + e.Message, e); end; end; end; |
AW: Problem mit Generator
mal ganz doof gefragt:
warum machst du in deiner AddEntry Routine den extrem seltsamen Umweg über ein konstantes Select Sql und überlässt dann den Dataset Komponenten da irgendwas mit Append zu machen? Warum umständlich wenn man es auch einfach über einen Insert Befehl gehen würde ... (und der könnte dann über returning auch den gerade generierten wert vom Generator zurückmelden, so das du den auch in deinem Dataset zur weiteren Identifikation des Datensatzes haben kannst. |
AW: Problem mit Generator
Zitat:
Grüße |
AW: Problem mit Generator
Zitat:
auf Basis einer Forumsdiskussion hier gemacht habe. Das funktioniert ja auch grundsätzlich. Nur irgendwie im Ablauf des Update Mechanismus scheint es ein Problem zu geben. |
AW: Problem mit Generator
Zitat:
hier die Generator Kopieren Routine:
Delphi-Quellcode:
Grüße
procedure TDBCopy.CopyGenerators;
var Generators : TStringList; Generator : string; Value : Int64; Msg : string; begin Generators := TStringList.Create; try FSourceSQLConnection.GetGeneratorNames('', '', '', Generators); for Generator in Generators do begin Value := GetCurrentGeneratorValue(FGeneratorReadQuery, Generator); SetGeneratorValue(FGeneratorWriteQuery, Generator, Value); Msg := 'Generator Wert gesetzt für: ' + Generator + 'Wert: ' + Value.ToString; FLog.Add(Msg); log.Send(LevelViolet, cCategory, Msg); end; finally Generators.Free; end; end; function TDBCopy.GetCurrentGeneratorValue(Query: TFDQuery; const GeneratorName: string): Int64; begin Query.Open('SELECT GEN_ID(' + GeneratorName +', 0) AS GENERATOR_VALUE '+ 'FROM RDB$DATABASE'); try if not Query.Eof then Result := Query.FieldByName('GENERATOR_VALUE').AsLargeInt else Result := -1; finally Query.Close; end; end; procedure TDBCopy.SetGeneratorValue(Query : TFDQuery; const GeneratorName : string; Value : Int64); begin try Query.ExecSQL('ALTER SEQUENCE ' + GeneratorName + ' RESTART WITH ' + Value.ToString); except on e:Exception do log.SendException(cCategory, 'Fehler beim Setzen des Generators: ' + GeneratorName + ' Wert: ' + Value.ToString + ' Grund: ' + e.Message, e); end; end; TurboMagic |
AW: Problem mit Generator
Noch ein sachdienlicher Hinweis:
im Rahmen dieses Updateablaufes wird von Firebird 2.5.x auf 5.0 aktualisiert. Ich hoffe mal nicht, dass das hier eine Rolle spielt, denn wie gesagt: nach einmaligem Fehler funktioniert die weitere Verbuchung in der neuen Version problemfrei. Grüße TurboMagic |
AW: Problem mit Generator
Zitat:
P.S.: ich meine, es gab eine Änderung bei FB4, was den Startwert eines Generators betrifft. Denn das Problem betrifft - wenn ich das richtig sehe - immer den letzten Datensatz. Setz doch probehalber den Startwert auf "Value + 1" und schau, was passiert. |
AW: Problem mit Generator
Oder statt
Delphi-Quellcode:
für Value.ToString
Query.ExecSQL('ALTER SEQUENCE ' + GeneratorName + ' RESTART WITH ' + Value.ToString);
Delphi-Quellcode:
nehmen.
select Max(id) + 1 from tabelle
Oder, da es ja bei diesem Update ein grundsätzliches Problem ist:
Delphi-Quellcode:
Value := 1 + GetCurrentGeneratorValue(FGeneratorReadQuery, Generator);
Oder hier einfach den nächsten Generatorwert auslesen, also sinngemäß:
Delphi-Quellcode:
Value := GEN_ID(GeneratorName, 1);
Das Problem scheint zu sein, dass in der "alten" Datenbank der Wert des Generators der zuletzt vergebene Wert zu sein scheint, während die "neue" Datenbank diesen Wert als den nächsten zu vergebenden Wert betrachtet. Beim ersten Lesen des Generatorwertes kracht es daher einmalig. Danach ist der Generatorwert dann "höher" als die letzte ID und somit ist der Fehler (unelegant) behoben. Grob formuliert: Alte Datenbank erhöht den Wert des Generators und gibt ihn dann aus. Neue Datenbank gibt den Wert des Generators aus und erhöht ihn dann. Daraus resultiert die einmalig "doppelte" Ausgabe eines Wertes, was dann nach einem einmaligen Fehler "korrigiert" erscheint. |
AW: Problem mit Generator
Hallo,
danke an alle für den Tipp mit Firebird 4.0. Das hab' ich damals scheinbar in der Änderungshistorie überlesen. Ich schaue mir diese und eure Lösungsvorschläge in der nächsten Programmiersitzung für die betreffende Anwendung gleich an. Grüße TurboMagic |
AW: Problem mit Generator
Hallo,
ok, ich hab' jetzt schon die Stelle in den FB 4.0.4 relase notes gefunden. Das Verhalten von Sequences wurde tatsächlich abgeändert. Ich schau' mir jetzt an, wie ich damit umgehe. Grüße TurboMagic |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:19 Uhr. |
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