Autor Beitrag
NOS
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 183
Erhaltene Danke: 2

Win XP, Win Vista Ultimate, Win 7 Ultimate
Delphi 19.4 - Sydney
BeitragVerfasst: 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 user profile iconChristian S.: Topic aus Datenbanken (inkl. ADO.NET) verschoben am Mi 13.07.2016 um 22:37
Nersgatt
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1581
Erhaltene Danke: 279


Delphi 10 Seattle Prof.
BeitragVerfasst: Do 14.07.16 06:48 
Klar, kann man mehr als einen Datensatz auf einmal einfügen:

ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 792
Erhaltene Danke: 49

Windows 7 / 10; CentOS 7; LinuxMint
Delphi 7-XE10.1, VS 2015
BeitragVerfasst: Do 14.07.16 07:06 
user profile iconNersgatt hat folgendes geschrieben Zum zitierten Posting springen:
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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1581
Erhaltene Danke: 279


Delphi 10 Seattle Prof.
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 233
Erhaltene Danke: 19

Win 7, Win 10
C#, ASP-MVC (VS 2017 Community), MS SQL, Firebird SQL
BeitragVerfasst: Do 14.07.16 08:08 
Moin
user profile iconLemmy hat folgendes geschrieben Zum zitierten Posting springen:


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
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 792
Erhaltene Danke: 49

Windows 7 / 10; CentOS 7; LinuxMint
Delphi 7-XE10.1, VS 2015
BeitragVerfasst: Do 14.07.16 09:18 
user profile iconerfahrener Neuling hat folgendes geschrieben Zum zitierten Posting springen:
Moin
user profile iconLemmy hat folgendes geschrieben Zum zitierten Posting springen:


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!?


dann zeigs mir - ich lerne gerne dazu..... Aber bitte mit Firebird, nicht mit MySQL ;-)
Lemmy
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 792
Erhaltene Danke: 49

Windows 7 / 10; CentOS 7; LinuxMint
Delphi 7-XE10.1, VS 2015
BeitragVerfasst: Do 14.07.16 09:20 
user profile iconNersgatt hat folgendes geschrieben Zum zitierten Posting springen:

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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 233
Erhaltene Danke: 19

Win 7, Win 10
C#, ASP-MVC (VS 2017 Community), MS SQL, Firebird SQL
BeitragVerfasst: 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 :gruebel: :les:
Und mein Gefühl hat sich bestätigt...
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1581
Erhaltene Danke: 279


Delphi 10 Seattle Prof.
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 183
Erhaltene Danke: 2

Win XP, Win Vista Ultimate, Win 7 Ultimate
Delphi 19.4 - Sydney
BeitragVerfasst: 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 ????

ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1581
Erhaltene Danke: 279


Delphi 10 Seattle Prof.
BeitragVerfasst: Do 14.07.16 10:06 
user profile iconNOS hat folgendes geschrieben Zum zitierten Posting springen:
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
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 792
Erhaltene Danke: 49

Windows 7 / 10; CentOS 7; LinuxMint
Delphi 7-XE10.1, VS 2015
BeitragVerfasst: Do 14.07.16 10:06 
user profile iconNOS hat folgendes geschrieben Zum zitierten Posting springen:
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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 183
Erhaltene Danke: 2

Win XP, Win Vista Ultimate, Win 7 Ultimate
Delphi 19.4 - Sydney
BeitragVerfasst: Do 14.07.16 10:19 
Du meinst also ich soll den Teil hier aus der Schleife lassen

ausblenden Delphi-Quelltext
1:
conquery.Sql.Text := 'INSERT INTO URLSourceTable(PARENTID, URL, URLSOURCE) VALUES (:PARENTID, :URL, :URLSOURCE)';					


Das multithreaded adden funktioniert über connection pooling
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 792
Erhaltene Danke: 49

Windows 7 / 10; CentOS 7; LinuxMint
Delphi 7-XE10.1, VS 2015
BeitragVerfasst: Do 14.07.16 10:49 
user profile iconNOS hat folgendes geschrieben Zum zitierten Posting springen:
Du meinst also ich soll den Teil hier aus der Schleife lassen


Jens hat es doch deutlich geschrieben:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
  //außerhalb der schleife
  conquery.Sql.Text := 'INSERT INTO URLSourceTable(PARENTID, URL, URLSOURCE) VALUES (:PARENTID, :URL, :URLSOURCE)';
....

//innerhalb der schleife

       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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 183
Erhaltene Danke: 2

Win XP, Win Vista Ultimate, Win 7 Ultimate
Delphi 19.4 - Sydney
BeitragVerfasst: 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

ausblenden volle Höhe 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:
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:
 // general options
  con.LoginPrompt := false;
  con.ConnectionDefName := ConDefName;
  // Tx options
  //con.TxOptions.Isolation := xiSnapshot;
  con.TxOptions.ReadOnly := false;
  con.TxOptions.AutoCommit := true;
  con.TxOptions.AutoStart := true;
  con.TxOptions.AutoStop := true;
  //con.TxOptions.DisconnectAction := xdCommit;
  // update options
  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.RefreshMode := rmOnDemand;
  //con.UpdateOptions.LockMode := lmPessimistic;
  con.UpdateOptions.LockWait := true;
  //con.UpdateOptions.LockPoint := lpImmediate;
  // ressource options
  con.ResourceOptions.CmdExecMode := amBlocking;
  con.ResourceOptions.MacroExpand := false;
  con.ResourceOptions.CmdExecTimeout := 60000;
  con.ResourceOptions.DirectExecute := true;
  // error handler
  con.OnError := FBConOnErrorHandler;
  con.connected := true;
  {$IFDEF CONNECTIONINTERFACEMONITORING}
   con.ConnectionIntf.Tracing := True;
  {$ENDIF}
  // set query options and add connection
  conquery.Connection := con;
  // general options
  conquery.CachedUpdates := false;
  // fetch options
  //conquery.FetchOptions.Mode := fmOnDemand;
  conquery.FetchOptions.AutoClose := false;
  // update options
  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.RefreshMode := rmOnDemand;
  //conquery.UpdateOptions.LockMode := lmPessimistic;
  conquery.UpdateOptions.LockWait := true;
  //conquery.UpdateOptions.LockPoint := lpImmediate;
  // ressource options
  conquery.ResourceOptions.CmdExecMode := amBlocking;
  conquery.ResourceOptions.MacroExpand := false;
  conquery.ResourceOptions.CmdExecTimeout := 60000;
  conquery.ResourceOptions.DirectExecute := true;
  // error handler
  conquery.OnPostError := FBOnPostErrorHandler;
  conquery.OnEditError := FBOnEditErrorHandler;
  conquery.OnError := FBOnErrorHandler;
NOS Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 183
Erhaltene Danke: 2

Win XP, Win Vista Ultimate, Win 7 Ultimate
Delphi 19.4 - Sydney
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1581
Erhaltene Danke: 279


Delphi 10 Seattle Prof.
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 183
Erhaltene Danke: 2

Win XP, Win Vista Ultimate, Win 7 Ultimate
Delphi 19.4 - Sydney
BeitragVerfasst: 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 ?