Datenbank: Firebird • Version: 2.5 • Zugriff über: IBDAC
SQL "Update or Insert" langsam
Hallo,
ich führe ca. 2000 "Update or Insert"-Statements mit einem TIBCQuery aus. Das dauert ca. 2,5 Sekunden und ist zu langsam. Ist das normal, dass das so lange dauert oder kann man das beschleuningen? (Zum Vergleich: Wenn ich die selben Daten ohne Datenbank in eine Ini-Datei schreibe ist das in 0,5 Sekunden erledigt.)
Code:
Transaction.StartTransaction;
for i := 0 to 2000 do begin Query.SQL.Text := 'Update or insert into foo (A, B, C) values (:a, :b, :c)'; Query.ParamByName('a').AsString := 'v1'; Query.ParamByName('b').AsString := 'v2'; Query.ParamByName('c').AsString := 'v3'; Query.Execute; end; Transaction.Commit; |
AW: SQL "Update or Insert" langsam
Wieso setzt Du innerhalb der Schleife jedesmal die SQL-Eigenschaft neu? Und fehlt da nicht ein MATCHING in Deinem Statement?
|
AW: SQL "Update or Insert" langsam
Zitat:
Siehe https://firebirdsql.org/refdocs/lang...or-insert.html |
AW: SQL "Update or Insert" langsam
Zitat:
|
AW: SQL "Update or Insert" langsam
Durch die dauernde Zuweisung des Statemants wird die Komponente jedes mal ein Unprepare schicken.
Dadurch geht der Vorteil der Parameter natürlich flöten... Frank |
AW: SQL "Update or Insert" langsam
Ggf im SQL das Matching oder andere "Hilfsmittel" ergänzen. Auch wenn es dann doppeltgemoppelt ist, könnte es dem Optimizer eine Hilfestellung sein.
Ohne weiteres wirkt ja ein Update erstmal auf den gesamten Datenbestand, ist also eine fette Nummer. Wirklich flott ist wahrscheinlich eine komplette Umstellung auf: Step1: Insert into <table> (<fields>) select <values> from table where not exists .. (bzw. in richtig mit self join und is null Prüfung) Step2: Updates mit ähnlichem Vorgehen. Wäre die Frage, ob es ständig geschieht und nervt oder die bequeme Formulierung oben eine einmalige Sache ist und mit ein paar Tränchen verdrücken zu bewältigen ist. |
AW: SQL "Update or Insert" langsam
Zitat:
Delphi-Quellcode:
Transaction.StartTransaction;
Query.SQL.Text := 'Update or insert into foo (A, B, C) values (:a, :b, :c)'; for i := 0 to 2000 do begin Query.ParamByName('a').AsString := 'v1'; Query.ParamByName('b').AsString := 'v2'; Query.ParamByName('c').AsString := 'v3'; Query.Execute; end; Transaction.Commit; |
AW: SQL "Update or Insert" langsam
Zitat:
Delphi-Quellcode:
Erspart auch noch dreimal pro Runde das Auswerten von ParamByName.
Transaction.StartTransaction;
Query.SQL.Text := 'Update or insert into foo (A, B, C) values (:a, :b, :c)'; ParamA = Query.ParamByName('a'); ParamB = Query.ParamByName('b'); ParamC = Query.ParamByName('c'); for i := 0 to 2000 do begin ParamA.AsString := 'v1'; ParamB.AsString := 'v2'; ParamC.AsString := 'v3'; Query.Execute; end; Transaction.Commit; |
AW: SQL "Update or Insert" langsam
Delphi-Quellcode:
geht es nicht auch so?
Transaction.StartTransaction;
Query.SQL.Text := 'Update or insert into foo (A, B, C) values (:a, :b, :c)'; Query.Params[0].DataType := ftString; Query.Params[1].DataType := ftString; Query.Params[2].DataType := ftString; for i := 0 to 2000 do begin Query.Params[0].Value := 'v1'; Query.Params[1].Value := 'v2'; Query.Params[2].Value := 'v3'; Query.Execute; end; Transaction.Commit; Erspart das Auswerten & Überprüfen des Typs sowie das Suchen der Parameter Position. |
AW: SQL "Update or Insert" langsam
Hallo,
bei IBDAC muss man explizit preparen, um Parameter ausnutzen zu können
Delphi-Quellcode:
Und dem Link von SProske zufolge spielt auch der Primary Key eine Rolle,
Transaction.StartTransaction;
Query.SQL.Text := 'Update or insert into foo (A, B, C) values (:a, :b, :c)'; // das fehlte Query.Prepare; for i := 0 to 2000 do begin Query.ParamByName('a').AsString := 'v1'; Query.ParamByName('b').AsString := 'v2'; Query.ParamByName('c').AsString := 'v3'; Query.Execute; end; Transaction.Commit; wie sieht denn die Tabellenstruktur aus? |
AW: SQL "Update or Insert" langsam
Zitat:
B VarChar(50) PK C Blob Text 80 Muss ich das SQL also anpassen? |
AW: SQL "Update or Insert" langsam
Hallo,
das heisst also, dass der PK aus 2 Feldern (A,B) besteht, dann ist das ja OK. Wies sieht es mit "meinem" Prepare aus? |
AW: SQL "Update or Insert" langsam
Kann man nicht einfach ein paar hundert oder sogar tausend Update-Insert-Anweisungen in einem Rutsch sammeln und die dann auf einmal executen? Wenn ich so grosse Mengen auf einmal aktualisieren und/oder einfügen muss, geht das immer viel schneller. Man kann doch in die SQL-Stringlisten einen Haufen Zeugs reinpacken, eigentlich bis der Speicher voll ist. Was da so aufhält, ist bestimmt das Execute 2000 Mal ausführen. Eigentlich sollte es auch reichen, das nur einmal auszuführen, 2000 Datensätze ist jetzt nicht die Welt.
Delphi-Quellcode:
Transaction.StartTransaction;
for i := 0 to 2000 do begin Query.SQL[i] := 'Update or insert into foo (A, B, C) values (:a, :b, :c)'; Query.ParamByName('a').AsString := 'v1'; Query.ParamByName('b').AsString := 'v2'; Query.ParamByName('c').AsString := 'v3'; end; Query.Execute; Transaction.Commit; |
AW: SQL "Update or Insert" langsam
Hallo,
FB kann kein bulk insert. Hier könnte man sich mit external tables behelfen. Aber ich wäre ja noch auf eine Antwort zu meinem prepare. |
AW: SQL "Update or Insert" langsam
Zitat:
Da bin ich auf den ersten Ärger mit Umlauten und/oder Codepages gespannt. Gruß K-H |
AW: SQL "Update or Insert" langsam
2 PKs geht nicht, aber ein zusammengesetzter über 2 Felder schon ;)
|
AW: SQL "Update or Insert" langsam
Zitat:
Zitat:
Am interessantesten finde ich aber ein BLOB Text(80), der in das "Upsert" involviert ist. Wie sieht es da mit Indizierung aus? Und wenn vorhanden, greift sie? Wenn nicht, was ich mir ganz gut vorstellen kann, bedeutet jedes einzelne "Upsert" ein Fullscan. Das dann in einer Schleife und die Performance ist im A.. |
AW: SQL "Update or Insert" langsam
Zugriff erfolgt über: IBDAC
kurz mal geguckt: https://www.devart.com/ibdac/docs/?batchops.htm Batch INSERT operation sample Let’s try to insert 1000 rows to the BATCH_TEST table using a Batch Insert operation:
Delphi-Quellcode:
var
i: Integer; begin // describe the SQL query IBCQuery1.SQL.Text := 'INSERT INTO BATCH_TEST VALUES (:ID, :F_INTEGER, :F_FLOAT, :F_STRING, :F_DATE)'; // define the parameter types passed to the query : IBCQuery1.Params[0].DataType := ftInteger; IBCQuery1.Params[1].DataType := ftInteger; IBCQuery1.Params[2].DataType := ftFloat; IBCQuery1.Params[3].DataType := ftString; IBCQuery1.Params[4].DataType := ftDateTime; // specify the array dimension: IBCQuery1.Params.ValueCount := 1000; // populate the array with parameter values: for i := 0 to IBCQuery1.Params.ValueCount - 1 do begin IBCQuery1.Params[0][i].AsInteger := i + 1; IBCQuery1.Params[1][i].AsInteger := i + 2000 + 1; IBCQuery1.Params[2][i].AsFloat := (i + 1) / 12; IBCQuery1.Params[3][i].AsString := 'Values ' + IntToStr(i + 1); IBCQuery1.Params[4][i].AsDateTime := Now; end; // insert 1000 rows into the BATCH_TEST table IBCQuery1.Execute(1000); end; |
AW: SQL "Update or Insert" langsam
Da kapselt dann aber IBDAC die Batch-Operation.
Das gibt es bei Firebird keine direkte Unterstützung in der API. Gruß Björn |
AW: SQL "Update or Insert" langsam
Zitat:
Mittels Transaktionshandling sollte es eigentlich wurscht sein, was an Spezialitäten da ist. Wenn man eine Reihe von Befehlen absetzt, werden sie erst mit "Commit" commited. Viele Commits, nach jedem insert bspw, bringen auch eine Verzögerung. Blockweise commit sind schneller, ist aber glaub ich marginal. |
AW: SQL "Update or Insert" langsam
Zitat:
Firebird kann zum Beispiel nicht die "Indexpflege" erst beim Commit machen und damit nur einmal für alle Commits. Es kann auch sein, dass viele Statements im Batch langsamer sind. Wenn eine weitere ältere Transaktion alte Versionen der Sätze noch im Fokus hat, muss der Server neue Pages allozieren um die neuen Versionen der Sätze zu speichern. Beim Insert ist es klar, dass er das immer machen muss, aber bei mehreren Updates kann er die alten Speicherbereiche recyceln. Das wird bei größeren Satzlängen problematischer, also insbesondere beim Updaten von größeren BLOBs, die ja dann ggf. auf eigene Pages pro BLOB ausgelagert werden zusätzlich zu den Pages, die er beschreiben muss, um den Satz zu speichern. Diese Phänomene können obigen Geschwindigkeitsvorteil wieder wett machen. Natürlich gibt es aber Gründe für eine derartige Implementierung und das ist die Datensicherheit und den Erhalt der Konsistenz der Datenbankdatei zu jedem Zeitpunkt. Bei Datenbanken sollte immer die Datensicherheit höher bewertet werden, als die Schnelligkeit. Gruß Björn |
AW: SQL "Update or Insert" langsam
Zitat:
Indizierungseffekte bspw. sind wieder eine andere Geschichte, defferable Indices sind auch interessant, aber nicht in jedem RDBMS verfügbar. Interessant an deinen Ausführungen auch die BLOB Thematik, die ja vielleicht das Problem des TE berührt. Aber der ist ja wohl abgetaucht. |
AW: SQL "Update or Insert" langsam
Bei Firebird kann man Indizes temporär deaktivieren:
SQL-Code:
Allerdings nicht bei aktiven Constraints.
ALTER INDEX <Indexname> INACTIVE;
|
AW: SQL "Update or Insert" langsam
Ah, wie wirkt sich das aus?
Nur auf die Aktualisierung oder auch auf die Nutzung bei Abfragen? Die Einschränkung auf "ohne aktive Constraints" ist natürlich hart, wäre dann aber immerhin für reine Suchindices möglich. Die Einschränkung deutet auch darauf hin, dass der Index nicht nur für Aktualisierungen deaktiviert ist, sondern auch für Abfragen. |
AW: SQL "Update or Insert" langsam
Hallo,
das stimmt. Einen reinen Such-Index kann man mit INACTIVE von Nutzung und Aktualisierung abschalten. Funktioniert aber nicht für Indizes, die für Fremdschlüssel erzeugt wurden oder für Indizes, die für Constraints erzeugt wurden oder für UNIQUE-Indizes, was ja auch ein Constraint ist. Somit ist die Funktion bei einer "ordentlichen" Datenbankstruktur mit Fremdschlüsseln in der Praxis eher irrelevant. Es kann aber auch nichts passieren, wenn man versucht, einen Index auf INACTIVE zu setzen, der in einem Constraint benötigt wird. Man bekommt einen Hinweis. Eigentlich nutze ich das umsetzen nur für die Erneuerung der Statistken der Indizes beim "Aufräumen" der Datenbank. |
AW: SQL "Update or Insert" langsam
da hier aber ein Update or Insert durchgeführt wird ,
ist das eher kontraproduktiv, da ohne Index die Prüfung auf das Update eine Full Table Scan auslösen wird mfg Hannes |
AW: SQL "Update or Insert" langsam
Zitat:
ja, wenn man den Index deaktivieren könnte. Der Index kann nicht auf INACTIVE gesetzt werden. Primary-Key ist ein Constraint. Gruß Björn |
AW: SQL "Update or Insert" langsam
Es kommt auch darauf an, ob die Daten einer gewissen Überprüfung bedürfen. Falls Daten 1:1 eingespielt werden können kann man Indizes/Contraints problemlos Deaktivieren.
|
AW: SQL "Update or Insert" langsam
Mmh, also in dem Fall hier wo PK betroffen sind, würde ich das Deaktivieren von Constraints mal ausschließen.
Generell ist beim Deaktivieren von Indizes oder Constraints ja auch zu berücksichtigen, wer/was sonst noch grad im System geschieht. Ebenfalls können aber neben den PK Keys und sonstigen Constraints auch reine Suchindices vorhanden sein. Die wären dann m.E. geeignet, sie zu deaktivieren. Dann muss im Zweifel mal jemand etwas länger auf einen Report oder eine Antwort der Suchmaske warten. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:53 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