Autor |
Beitrag |
NOS
Beiträge: 183
Erhaltene Danke: 2
Win XP, Win Vista Ultimate, Win 7 Ultimate
Delphi 19.4 - Sydney
|
Verfasst: Mi 13.07.16 22:14
Hallo zusammen,
ich schreibe datensätze in einer schleife in eine firebird embedded db ... das ganze multithreaded ... ich frage mich ob es geschwindigkeit bringt und ob es überhaupt die möglichkeit gibt das hinzufügen der Datensätze oder besser gesagt mehrerer datensätze am stück zu erledigen ... ich nhutze firebird embedded in der letzten 2.5.x release mit firedac und delphi berlin
Ich würde mich freuen wenn ihr mir helfen könntet.
LG,
Andreas Moderiert von Christian S.: Topic aus Datenbanken (inkl. ADO.NET) verschoben am Mi 13.07.2016 um 22:37
|
|
Nersgatt
Beiträge: 1581
Erhaltene Danke: 279
Delphi 10 Seattle Prof.
|
Verfasst: Do 14.07.16 06:48
Klar, kann man mehr als einen Datensatz auf einmal einfügen:
SQL-Anweisung 1: 2: 3: 4: 5:
| INSERT INTO tabelle (ID, FELD) VALUES (1, 'BLA'), (2, 'BLUBB'); |
Aber pass auf, die maximale Größe der Statements ist beschränkt.
Wenn Du viele Datensätze einfügst, dann kann das durchaus Geschwindigkeitsvorteile bringen.
_________________ Gruß, Jens
Zuerst ignorieren sie dich, dann lachen sie über dich, dann bekämpfen sie dich und dann gewinnst du. (Mahatma Gandhi)
|
|
Lemmy
Beiträge: 792
Erhaltene Danke: 49
Windows 7 / 10; CentOS 7; LinuxMint
Delphi 7-XE10.1, VS 2015
|
Verfasst: Do 14.07.16 07:06
Nersgatt hat folgendes geschrieben : | Klar, kann man mehr als einen Datensatz auf einmal einfügen: |
das mag vielleicht für MySQL und co gelten, aber in Firebird wäre mir das neu....
|
|
Nersgatt
Beiträge: 1581
Erhaltene Danke: 279
Delphi 10 Seattle Prof.
|
Verfasst: Do 14.07.16 07:10
Ich war mir sehr sicher, dass Firebird das auch kann. Ist schon wieder 2 1/2 Jahre her, dass ich nicht mehr mit Firebird arbeite...
Aber auch für Firebird gibt es eine Lösung: www.firebirdfaq.org/faq336/
Ich würde dabei die Execute Block-Lösung bevorzugen.
_________________ Gruß, Jens
Zuerst ignorieren sie dich, dann lachen sie über dich, dann bekämpfen sie dich und dann gewinnst du. (Mahatma Gandhi)
|
|
erfahrener Neuling
Beiträge: 233
Erhaltene Danke: 19
Win 7, Win 10
C#, ASP-MVC (VS 2017 Community), MS SQL, Firebird SQL
|
Verfasst: Do 14.07.16 08:08
Moin Lemmy hat folgendes geschrieben : |
das mag vielleicht für MySQL und co gelten, aber in Firebird wäre mir das neu.... |
Wieso sollte es denn nicht gehen? die Syntax von Nersgatt war doch richtig!?
Gruß
Julian
|
|
Lemmy
Beiträge: 792
Erhaltene Danke: 49
Windows 7 / 10; CentOS 7; LinuxMint
Delphi 7-XE10.1, VS 2015
|
Verfasst: Do 14.07.16 09:18
|
|
Lemmy
Beiträge: 792
Erhaltene Danke: 49
Windows 7 / 10; CentOS 7; LinuxMint
Delphi 7-XE10.1, VS 2015
|
Verfasst: Do 14.07.16 09:20
Nersgatt hat folgendes geschrieben : |
Ich würde dabei die Execute Block-Lösung bevorzugen. |
stimmt das wäre in der Tat eine Variante. wäre interessant ob diese wirklich schneller ist als über einen Insert mit manueller Tranaktionssteuerung....
|
|
erfahrener Neuling
Beiträge: 233
Erhaltene Danke: 19
Win 7, Win 10
C#, ASP-MVC (VS 2017 Community), MS SQL, Firebird SQL
|
Verfasst: Do 14.07.16 09:25
Naja wie schon gesagt: Die von Nersgatt gepostete Syntax ist richtig (also insert into Tabelle (var1, var2, ...) values (value1, value2, ...) und wird auch öfters von mir verwendet. Allgemein hab ich bis jetzt noch gar keine so großen Unterschiede zu SQL entdecken können. (arbeite jetzt vllt seit 2 Monaten damit)
Langsam kriege ich aber das Gefühl, dich und den Sachverhalt missverstanden zu haben
Und mein Gefühl hat sich bestätigt...
|
|
Ralf Jansen
Beiträge: 4705
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Do 14.07.16 09:38
Ich bezweifle das bei Firebird Embedded es irgendeinen größeren Unterschied gibt ob ich die Datensätze einzeln oder en bloc zur Engine schiebe. Wir bewegen uns schließlich im gleichen Prozessraum. Und die Engine wird letztlich beim einfügen mit den Daten das gleiche je Datensatz tun egal ob ich ein Single Datensatz Statement dahingeschickt habe oder ein Multi Datensatz Statement.
|
|
Nersgatt
Beiträge: 1581
Erhaltene Danke: 279
Delphi 10 Seattle Prof.
|
Verfasst: Do 14.07.16 09:44
Einen Versuch ist es immerhin wert, ob es Performancevorteile bringt.
Wenn man einen Datensatz mit einem Statement einfügt, sollte man aber auf jeden Fall ein Prepared-Statement nutzen. Sonst muss die Datenbank ja jedes Statement erneut parsen, optimieren, usw., was definitiv spürbar Performance kostet.
_________________ Gruß, Jens
Zuerst ignorieren sie dich, dann lachen sie über dich, dann bekämpfen sie dich und dann gewinnst du. (Mahatma Gandhi)
|
|
NOS
Beiträge: 183
Erhaltene Danke: 2
Win XP, Win Vista Ultimate, Win 7 Ultimate
Delphi 19.4 - Sydney
|
Verfasst: Do 14.07.16 10:01
Zunächst einmal vielen Dank euch allen ... wenn ich es recht verstehe ist diese Variante also sehr langsam zum adden in die db ????
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| try conquery.Params.Clear; conquery.Sql.Text := 'INSERT INTO URLSourceTable(PARENTID, URL, URLSOURCE) VALUES (:PARENTID, :URL, :URLSOURCE)'; conquery.ParamByName('PARENTID').AsInteger := FCurrentID; conquery.ParamByName('URLSOURCE').AsString := FURLSource.DataString; conquery.ParamByName('URL').AsString := FCurrentURL; conquery.ExecSql; conquery.Close; except on E : Exception do if DomainCrawler.LOGMode = lmFile then DomainCrawler.WriteToLOGFile('CoreEngine Error 100080 (UrlworkerExecute - Update URLSourceTable) - [' + IntToStr(ThreadID) + '] - ' + E.Message); end; |
|
|
Nersgatt
Beiträge: 1581
Erhaltene Danke: 279
Delphi 10 Seattle Prof.
|
Verfasst: Do 14.07.16 10:06
NOS hat folgendes geschrieben : | Zunächst einmal vielen Dank euch allen ... wenn ich es recht verstehe ist diese Variante also sehr langsam zum adden in die db ????
|
Nein, nicht unbedingt.
Nur solltest Du drauf achten, dass Du Zeile 2 und 3 nur einmal ausführst. Zeile 4 -7 packst Du in Deine Schleife (befüllst die Parameter der Query also immer mit neuen Werten und fügst dann ein). Zeile 8 kannst Du Dir schenken.
_________________ Gruß, Jens
Zuerst ignorieren sie dich, dann lachen sie über dich, dann bekämpfen sie dich und dann gewinnst du. (Mahatma Gandhi)
|
|
Lemmy
Beiträge: 792
Erhaltene Danke: 49
Windows 7 / 10; CentOS 7; LinuxMint
Delphi 7-XE10.1, VS 2015
|
Verfasst: Do 14.07.16 10:06
NOS hat folgendes geschrieben : | Zunächst einmal vielen Dank euch allen ... wenn ich es recht verstehe ist diese Variante also sehr langsam zum adden in die db ????
|
naja, du schreibst da halt einen Datensatz in die DB. Weder das Umfeld noch der Thread ist hier erkennbar. Darüber hinaus kann meiner Meinung nach multithreaded Inserts in FIrebird embedded nicht funktionieren, weil auf die Embedded nur eine Connection erlaubt ist (zumindest in FB 2.5) - d.h. da wird dann die Connection irgend wann zum Flaschenhals.
Und wenn Du den gezeigten Code mehrfach (in einer Schleife) ausführst, dann solltest Du die SQL-Zuweisung außerhalb der Schleife machen, sonst verlierst Du den Vorteil der Parameter und die Query muss den Insert bei jeder Ausführung erneut parsen.
|
|
NOS
Beiträge: 183
Erhaltene Danke: 2
Win XP, Win Vista Ultimate, Win 7 Ultimate
Delphi 19.4 - Sydney
|
Verfasst: Do 14.07.16 10:19
Du meinst also ich soll den Teil hier aus der Schleife lassen
Delphi-Quelltext 1:
| conquery.Sql.Text := 'INSERT INTO URLSourceTable(PARENTID, URL, URLSOURCE) VALUES (:PARENTID, :URL, :URLSOURCE)'; |
Das multithreaded adden funktioniert über connection pooling
|
|
Martok
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: Do 14.07.16 10:44
Wie handhabt FBE Transaktionen? Wenn du parallel arbeitest muss da ja auch irgendwo eine Transaktion drumrum sein... falls da noch irgendwo ein Autocommit ist dürfte das das langsamste sein.
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
|
|
Lemmy
Beiträge: 792
Erhaltene Danke: 49
Windows 7 / 10; CentOS 7; LinuxMint
Delphi 7-XE10.1, VS 2015
|
Verfasst: Do 14.07.16 10:49
NOS hat folgendes geschrieben : | Du meinst also ich soll den Teil hier aus der Schleife lassen |
Jens hat es doch deutlich geschrieben:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| conquery.Sql.Text := 'INSERT INTO URLSourceTable(PARENTID, URL, URLSOURCE) VALUES (:PARENTID, :URL, :URLSOURCE)'; ....
conquery.ParamByName('PARENTID').AsInteger := FCurrentID; conquery.ParamByName('URLSOURCE').AsString := FURLSource.DataString; conquery.ParamByName('URL').AsString := FCurrentURL; conquery.ExecSql; |
Ob ein ClearParams notwendig ist bitte bei den Komponenten nachschlagen, habe ich noch nie gebraucht.
Und dann bitte auch den Einwurf von matrok beachten - bei so was ist manuelle Transactionsteuerung Pflicht und dürfte (wenn man nach jedem Insert ein Commit absetzt - bewusst oder unbewusst) die größte Bremse sein
|
|
NOS
Beiträge: 183
Erhaltene Danke: 2
Win XP, Win Vista Ultimate, Win 7 Ultimate
Delphi 19.4 - Sydney
|
Verfasst: Do 14.07.16 10:51
Die Connection hat in den Settings ein AutoCommit = true .... nun bin ich nicht der DB spezialist ... was bedeutet eine transaction drumrum ?
hier mal der Codeblock zum einrichten von conection und query
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: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67:
| con.LoginPrompt := false; con.ConnectionDefName := ConDefName; con.TxOptions.ReadOnly := false; con.TxOptions.AutoCommit := true; con.TxOptions.AutoStart := true; con.TxOptions.AutoStop := true; con.UpdateOptions.ReadOnly := false; con.UpdateOptions.EnableInsert := true; con.UpdateOptions.EnableUpdate := true; con.UpdateOptions.EnableDelete := false; con.UpdateOptions.RequestLive := true; con.UpdateOptions.UpdateMode := upWhereKeyOnly; con.UpdateOptions.UpdateChangedFields := true; con.UpdateOptions.CheckRequired := false; con.UpdateOptions.CheckReadOnly := false; con.UpdateOptions.CheckUpdatable := true; con.UpdateOptions.LockWait := true; con.ResourceOptions.CmdExecMode := amBlocking; con.ResourceOptions.MacroExpand := false; con.ResourceOptions.CmdExecTimeout := 60000; con.ResourceOptions.DirectExecute := true; con.OnError := FBConOnErrorHandler; con.connected := true; {$IFDEF CONNECTIONINTERFACEMONITORING} con.ConnectionIntf.Tracing := True; {$ENDIF} conquery.Connection := con; conquery.CachedUpdates := false; conquery.FetchOptions.AutoClose := false; conquery.UpdateOptions.ReadOnly := false; conquery.UpdateOptions.EnableInsert := true; conquery.UpdateOptions.EnableUpdate := true; conquery.UpdateOptions.EnableDelete := false; conquery.UpdateOptions.RequestLive := true; conquery.UpdateOptions.UpdateMode := upWhereKeyOnly; conquery.UpdateOptions.UpdateChangedFields := true; conquery.UpdateOptions.CheckRequired := false; conquery.UpdateOptions.CheckReadOnly := false; conquery.UpdateOptions.CheckUpdatable := true; conquery.UpdateOptions.LockWait := true; conquery.ResourceOptions.CmdExecMode := amBlocking; conquery.ResourceOptions.MacroExpand := false; conquery.ResourceOptions.CmdExecTimeout := 60000; conquery.ResourceOptions.DirectExecute := true; conquery.OnPostError := FBOnPostErrorHandler; conquery.OnEditError := FBOnEditErrorHandler; conquery.OnError := FBOnErrorHandler; |
|
|
NOS
Beiträge: 183
Erhaltene Danke: 2
Win XP, Win Vista Ultimate, Win 7 Ultimate
Delphi 19.4 - Sydney
|
Verfasst: Mi 27.07.16 20:46
Hallo zusammen,
also ich habe es nun soweit umgebaut dass das AutoCommit raus ist ... mal zum Verständnis und zum Speed
wenn ich also in jedem Thread, in dem ich ja in mehrere Tables schreibe, für jede Table eine Query erzeuge müsste ich doch, da die Query das SQL Statement nicht mehr parsen, muss auch massiv zeit sparen ... oder ?
|
|
Nersgatt
Beiträge: 1581
Erhaltene Danke: 279
Delphi 10 Seattle Prof.
|
Verfasst: Mo 01.08.16 15:44
Als Grundregel kann man sagen, dass es immer sinnvoll ist, wiederkehrende Sql-Statements nur einmal vorzubereiten und dann über Parameter immer neu zu befüllen.
Wie viel Performance man damit rausholt hängt immer vom Anwendungszweck ab.
Ich hab mal bei einem Arbeitgeber Importschnittstellen angepasst, wo gerne mal 1000 Datensätze importiert wurden.
Vorher war es so - wie man es oft bei Anfängern sieht - dass für jeden Datensatz ein Sql-String zusammen gebastelt wurde und an die Datenbank geschickt. Umgebaut zu einer parametrisierten Abfrage, einmal vorbereiten, 1000 mal befüllen -> schwupps ist die Importschnittstelle um den Faktor 10 schneller (gefühlt). Chef hat sich gefreut.
Bei solchen Konstellationen kann man also enorm einsparen.
_________________ Gruß, Jens
Zuerst ignorieren sie dich, dann lachen sie über dich, dann bekämpfen sie dich und dann gewinnst du. (Mahatma Gandhi)
|
|
NOS
Beiträge: 183
Erhaltene Danke: 2
Win XP, Win Vista Ultimate, Win 7 Ultimate
Delphi 19.4 - Sydney
|
Verfasst: Mo 01.08.16 15:49
Hallo Nersgatt,
danke für die Info ... ich habe es in den letzten Tagen so umgebaut dass es nun für jede Table in die ich während der Analyse schreibe auch eine separate TFDQuery gibt und ich das SQL Statement nur einmal zu Anfang vorbereite bzw. übergebe und das wars.
Zitat: | einmal vorbereiten, 1000 mal befüllen |
bedeutet aber trotz allem in diesem fall 1000 zugriffe auf die db oder ging das in einem rutsch ?
|
|