Entwickler-Ecke
Datenbanken - Delphi/Interbase -> Transaktionen
stiff - So 01.05.05 18:36
Titel: Delphi/Interbase -> Transaktionen
Hallo,
ich habe folgende Beispiel-Transaktion:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| ... if not m_update.Transaction.InTransaction then m_update.Transaction.StartTransaction;
if not m_update.ExecuteSQL('alter table abnehmer add mitglied char(1)') then goto L_ERROR; if not m_update.ExecuteSQL('update abnehmer set mitglied = ''N''') then goto L_ERROR; ... m_update.Transaction.Commit;
L_ERROR: m_Update.Transaction.Rollback; |
Das Feld 'mitglied' wird in die Tabelle eingefuegt, aber beim Updaten des Feldes kommt eine Fehlermeldung (conversion error from string 'N')!
Muss man wirklich nach jedem "alter table ..." ein 'commit' machen oder übersehe ich da was?
Die Transaktion sollte normalerweise ja dafür da sein, dass man mehrere Anweisungen entweder ganz oder gar nicht ausführt.
Bitte um Ratschläge
Gruß
stiff
Moderiert von
Christian S.: Code- durch Delphi-Tags ersetzt.
Lemmy - So 01.05.05 20:08
Hi,
ich würde Änderungen an der Struktur nicht mit DML-Anweisungen mischen. Ich empfehle Dir zudem, auf goto zu verzichten. Der Fehler hat meiner Meinung nach aber nichts mit der Transaktionssteuerung zu tun.... Versuch mal folgendes:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| if not m_update.Transaction.InTransaction then m_update.Transaction.StartTransaction;
try m_update.ExecuteSQL('alter table abnehmer add mitglied char(1)'); m_update.ExecuteSQL('update abnehmer set mitglied = '#39'N'#39); ... m_update.Transaction.Commit; except m_Update.Transaction.Rollback; end; |
Lemmy
stiff - Mo 02.05.05 16:30
Hallo Lemmy,
habe deinen Vorschlag implementiert, es kommt aber trotzdem immer noch dieselbe Fehlermeldung.
Das Vermischen von DDL- und DML-Anweisungen wäre für mich eines der wichtigsten Punkte bei Transaktionen,
da dies bei anderen Datenbanken (z.B. PostgreSQL) ja auch funktioniert.
Ich verwende eine TIBQuery für die SQL-Anweisung. Wenn ich der Query die Parameter manuell zuweise (also z.B. m_IBQuery.Params[0] := 'N'), dann kommt keine Fehlermeldung, jedoch bleibt die erhoffte Wirkung der SQL-Anweisung aus. (im obigen Beispiel wird Feld 'mitglied' nicht gesetzt!)
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:
| function TUpdate.ExecuteSQL(sql:string): boolean; begin m_IBQuery.Close; m_IBQuery.SQL.Clear; m_IBQuery.SQL.Text := sql; try m_IBQuery.ExecSQL; ExecuteSQL:=True except on E:Exception do begin MessageDlg(E.Message,mtError,[mbOK],E.HelpContext); ExecuteSQL:=False end end end; |
Ist TIBQuery die ideale Komponente zum Ausführen von SQL-Statements oder gibt es da Komponenten, welche geeigneter wären?
Stefan.Buchholtz - Mo 02.05.05 16:41
stiff hat folgendes geschrieben: |
Hallo Lemmy,
habe deinen Vorschlag implementiert, es kommt aber trotzdem immer noch dieselbe Fehlermeldung.
Das Vermischen von DDL- und DML-Anweisungen wäre für mich eines der wichtigsten Punkte bei Transaktionen,
da dies bei anderen Datenbanken (z.B. PostgreSQL) ja auch funktioniert.
|
DIe meisten Datenbanken, sogar Oracle und soweit ich weiss auch Interbase untestützen keine Transaktionen bei DDL-Anweisungen. Jede DDL-Anweisung wirkt als implizites Commit für vorherige DML-Anweisungen.
Stefan
stiff - Mo 02.05.05 18:20
Dann werde ich in diesem Fall wohl oder übel ohne Transaktionen auskommen müssen ... :(
Danke trotzdem für die Hinweise!
stiff
Lemmy - Di 03.05.05 07:35
Guten Morgen,
ja, es gibt wesentlich bessere Komponenten als TIBTable und TIBQuery. Diese wurden nur zu Umstellungszwecken (von BDE auf IBX) eingeführt und haben eigentlich sonst keinen Wert. Verwende entweder eine TIBDataSet oder eine TIBSQL (die gibt aber keine Werte zurück).
Lemmy
Delete - Di 03.05.05 09:14
stiff hat folgendes geschrieben: |
Dann werde ich in diesem Fall wohl oder übel ohne Transaktionen auskommen müssen ... :(
|
Das geht beim Interbase/Firebird wegen der MGA gar nicht. Sogar zum Lesen von Daten musst du eine Transaktion starten!
Amiga-Fan - Di 03.05.05 09:36
Zitat: |
Das geht beim Interbase/Firebird wegen der MGA gar nicht. Sogar zum Lesen von Daten musst du eine Transaktion starten! |
wie meinst du das? Muss man auch bei Select-Statements eine Transaktion machen? Bei mir hat es auch so funktioniert.
Delete - Di 03.05.05 10:18
Amiga-Fan hat folgendes geschrieben: |
Zitat: | Das geht beim Interbase/Firebird wegen der MGA gar nicht. Sogar zum Lesen von Daten musst du eine Transaktion starten! |
wie meinst du das? Muss man auch bei Select-Statements eine Transaktion machen? Bei mir hat es auch so funktioniert. |
Dann wird die Transaktion bei automatisch gestartet worden sein. Bei den IBX - Komponenten gibts die Option "AutoStartTransaction" (oder so ähnlich :gruebel:).
Amiga-Fan - Sa 25.11.06 00:12
warum muß man zum lesen eine Transaktion starten? habe wieder PRobleme damit...
Delete - Sa 25.11.06 14:21
Amiga-Fan hat folgendes geschrieben: |
warum muß man zum lesen eine Transaktion starten? |
Wegen der Multigenerationsarchitektur des Interbase/Firebird muss immer eine Transaktion gestartet werden, egal ob lesen oder etwas schreiben.
Amiga-Fan - Sa 25.11.06 17:06
im Moment habe ich wieder das Problem, das er mir anzeigt das eine Transaktion schon aktiv ist. Das kann aber einfach nicht sein, ich habe absolut jedes starttransaction bereits schon mit commit abgeschlossen. Ich versteh das nicht, ob das vielleicht damit zusammenhängt das ich die tibquery und ibx allgemein mit Firebird 1.5.3 benutze? Wie würde das beim lesen denn aussehen sollen?
Lesen:
starttransaction
open
commitretaining bzw. rollback
----------
Schreiben:
starttransaction
execsql
commit bzw rollback
Muß das so aussehen?
UGrohne - Sa 25.11.06 18:23
Ein CommitRetaining bedeutet, dass zwar committed wird, aber die Transaktion immer noch offen bleibt. Schau mal nach, ob Du bei den IBTransactions als DefaultAction CommitRetaining angegeben hast. Das kann diese Meldung hervorrufen.
Amiga-Fan - Sa 25.11.06 18:51
nein, commitretaining steht nicht drin, sondern commit.
wenn ich fürs lesen ein einfaches commit mache (statt commitretaining) hatte ich das Problem das anschließend keine Daten in der Query mehr drin waren.
Lemmy - So 26.11.06 13:51
Hi,
wenn Delphi sich beschwert, dass schon eine Transaktion gestartet ist, warum überprüfst Du das nicht vorher?
Delphi-Quelltext
1: 2: 3:
| if Transaction.InTransaction then Transaction.Commit; Transaction.StartTransaction; |
Lemmy
Amiga-Fan - So 26.11.06 16:25
nach jedem starttransaction mache ich bereits ein commit, wenn ich dann wieder ein starttransaction mache ist somit die Transaktion theorethisch bereits abgeschlossen.
das was du gezeigt hast, habe ich bereits versucht. Bei execsql erhielt ich dann eine Fehlermeldung
Lemmy - So 26.11.06 19:51
Amiga-Fan hat folgendes geschrieben: |
... die Transaktion theorethisch bereits abgeschlossen. |
eben... theoretisch, aber nicht praktisch!
Amiga-Fan hat folgendes geschrieben: |
das was du gezeigt hast, habe ich bereits versucht. Bei execsql erhielt ich dann eine Fehlermeldung |
Und....??? OutOfMemory? Äpfel nicht gefunden? Birnen faul? oder soll ich weiterraten? Du kannst auch stopp rufen sobald ich die richtige Fehlermeldung geraten habe.... ;-)
Amiga-Fan - So 26.11.06 20:07
Zitat: |
eben... theoretisch, aber nicht praktisch! |
wieviele commits muß ich denn machen, um da sicherzugehen? oder würdest du eher sowas wie
while transaction.intransaction do begin
end;
machen? Vor dem starttransaction oder nach dem commit?
im moment erhalte ich immer die meldung "Transaktion ist nicht aktiv", und das immer beim 2. Durchlaufen dieser Routine, beim commit:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38:
| try DBQuery.database.close; DBQuery.database.open;
if not DBQuery.database.Connected then begin DBQuery.database.open; if not DBQuery.database.Connected then begin DBQuery.database.open; if not DBQuery.database.Connected then begin result:=false; exit; end; end; end;
if assigned(dbtransaction) then DBQuery.Transaction:=DBTransaction; except result:=false; exit; end;
if assigned(dbtransaction) then DBTransaction.StartTransaction; try with DBQuery do begin close; if trim(DBQuery.SQL.text)<>trim(SQLStr) then begin sql.clear; sql.add(SQLStr); end; if pos('INSERT',ansiuppercase(SQLStr))>0 then begin [..] end; execsql; end; if assigned(dbtransaction) then DBTransaction.Commit; |
hansa - So 26.11.06 20:54
Was ist denn das für eine Funktion ? :shock:
Jetzt mal der Reihe nach :
Amiga-Fan hat folgendes geschrieben: |
..wieviele commits muß ich denn machen, um da sicherzugehen? |
Lediglich eines. Geht das schief, dann liegt sonstwo ein Fehler vor und auch while etc. nützt dann nichts mehr. Ich nehme mal an, du benutzt ein Form, um Daten einzugeben.
Delphi-Quelltext
1: 2: 3: 4: 5:
| if not DM.Transaction.Active then DM.Transaction.StartTransaction; ... if DM.Transaction.Active then DM.Transaction.Commit; |
Mehr ist das nicht. Du kannst das genausogut in einen "Speichern"-Button verfrachten. Dann müßte
zusätzlich noch in das OnClick :
Delphi-Quelltext
1: 2: 3: 4:
| ... if DM.Transaction.Active then begin DM.Transaction.Commit; DM.Transaction.StartTransaction; |
Amiga-Fan - So 26.11.06 21:17
nein ich benutze ein datamodul, in dem diese Funktion ist, und dieser Funktion übergebe ich den sql-befehl. Der Funktion übergebe ich die Query und die Transaktion (da mein Programm 2 austauschbare Datenbanken verwaltet).
wie man sieht, mache ich in dieser Funktion ein starttransaction und commit. Wenn ich übrigens if assigned(dbtransaction) then weglasse, bleibt das problem vorhanden...
naja ich weiß nicht, woran es liegt. Imho ist dieser code richtig...
hansa - So 26.11.06 21:37
Amiga-Fan hat folgendes geschrieben: |
...Der Funktion übergebe ich die Query und die Transaktion (da mein Programm 2 austauschbare Datenbanken verwaltet).
|
Inwiefern austauschbar ? Wann steht fest, welche DB gebraucht wird ? Muss die während des Programmlaufes gewechselt werden oder wie ? "Austauschen" heißt für mich, dass zu einer Zeit nur eine DB gebraucht wird. Du musst schon etwas mehr sagen. Ich vermute nämlich stark, dass sich die Problematik viel einfacher lösen läßt.
Amiga-Fan - So 26.11.06 21:53
"muß" gar nicht. Die aktive DB wird über einen Menüpunkt umgeschaltet. Man kann aber auch nur eine DB festlegen.
wenn ich die DB wechsle, lege ich über variablen die aktuelle db, die queries und die transaktion fest. Nachher greife ich auf diese Variablen zu und übergebe sie dieser prozedur. Grundsätzlich ist das nicht schwer, ich kann aber natürlich nicht den ganzen quellcode posten...
Bitte beachten, diese funktion im datamodul rufe ich mehrere male mit denselben parametern hintereinander auf (bis auf sql-befehl natürlich), und die Fehlermeldung tritt erst beim 2. aufruf auf...
hansa - Mo 27.11.06 00:56
Amiga-Fan hat folgendes geschrieben: |
...wenn ich die DB wechsle, lege ich über variablen die aktuelle db, die queries und die transaktion fest. Nachher greife ich auf diese Variablen zu und übergebe sie dieser prozedur.... |
So ungefähr war es zu erwarten. :mrgreen:
Ein Denkfehler produziert wohl einige Folgefehler. Es ist überhaupt kein Problem eine Datenbank zu benutzen und irgendwann eine andere. Ich habe hier bestimmt 10 verschiedene DBs. Test-DB, welche mit Originaldaten usw. Dabei wird gar nichts übergeben, außer dem ConnectionString zur DB. In der Praxis sieht es so aus, dass der DB-Name in einer INI im Programm-Verzeichnis liegt und die wird notfalls eben manuell geändert.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| Ini := TIniFile.Create (ExtractFilePath (ParamStr (0)) + 'DB.INI'); DatenBankName := Ini.ReadString('Datenbank','Name',ExtractFilePath (ParamStr (0))+'db\XXX.fdb') DM.DataBase.Close; DM.DataBase.DatabaseName := DatenBankName; DM.DataBase.Open; DM.Transaction.Active := true; Ini.free; |
Sollte Bedarf bestehen, die DB auch während des Programmlaufes zu wechseln, dann wäre der Code, der die INI betrifft auch leicht durch eine ComboBox etc. zu ersetzen. Es geht lediglich um : Database.Close -> genauen Datenbanknamen angeben -> Database.Open. Alles andere bleibt gleich. Würdest Du bei 10 DBs allen eine extra TDataBase, TTransaction usw. spendieren ? :shock:
Amiga-Fan - Mo 27.11.06 01:03
das programm ist sowieso auf 2 datenbanken festgelegt, das ist schon ok. Wenn es noch mehr wären, hätte ich wohl auch eine andere Lösung ausgesucht, aber so... außerdem will ich über eine funktion eine db in die andere kopieren können, dazu brauche ich zwei gleichzeitig gültige db-verbindungen.
die db-verbindungen schreibe ich auch in eine ini-datei, aber natürlich im anwendungsdaten-verzeichnis.
wie auch immer, es läuft jetzt wieder. Ich hatte beim dynamischen Generieren von Queries nicht die Transaktion zugewiesen, danach mußte ich noch in meiner Schreibfunktion ein db.close und anschließend db.open durchführen, und dann gings wieder.
hansa - Mo 27.11.06 01:16
Amiga-Fan hat folgendes geschrieben: |
...danach mußte ich noch in meiner Schreibfunktion ein db.close und anschließend db.open durchführen, und dann gings wieder. |
Überlege trotzdem, was ich geschrieben habe. Oder eben auch nicht. "Operation gelungen, Patient tot" das sollte keine Dauerlösung werden. :mrgreen:
Lemmy - Mo 27.11.06 08:01
Guten Morgen,
Amiga-Fan hat folgendes geschrieben: |
wie auch immer, es läuft jetzt wieder. Ich hatte beim dynamischen Generieren von Queries nicht die Transaktion zugewiesen, danach mußte ich noch in meiner Schreibfunktion ein db.close und anschließend db.open durchführen, und dann gings wieder. |
genau das habe ich gemeint: Der Fehler lag nicht an der Transaktion sondern wo anders und hat bis an diese Stelle einfach durchgeschlagen! Gut wenn es jetzt läuft...
Grüße
Lemmy
Amiga-Fan - Di 12.06.07 20:44
Ich weiß jetzt, woran es lag. Zum einen war AutoCommit aktiviert...
Zum anderen startete ich immer eine neue Transaktion, ohne zu achten ob bereits eine aktiv war.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!