WinFuture-Forum.de: Methode SQL CE Insert dauert ewig - WinFuture-Forum.de

Zum Inhalt wechseln

Nachrichten zum Thema: Entwicklung
Seite 1 von 1

Methode SQL CE Insert dauert ewig 35k Datensätze brauchen 1 Stunde zum einfügen


#1 Mitglied ist offline   der dom 

  • Gruppe: aktive Mitglieder
  • Beiträge: 569
  • Beigetreten: 14. Juni 12
  • Reputation: 73
  • Geschlecht:Männlich
  • Wohnort:Zuhause
  • Interessen:Mein Haus, meine IT, Programmierung

geschrieben 10. September 2013 - 00:33

Hi,

ich habe in einem bestehenden Projekt ein kleines Problem. Ich habe knapp 35k Datensätze die es in eine SQL CE Datenbank zu packen gilt.

Großes Problem: Die Dauer.

Je nach Funktionen die ich einbaue, dauert der INSERT über eine Stunde.

Ich weiß, dass bei dem MS SQL Bulk Copy das ganze nur wenige Sekunden bis max. 2 Minuten dauert. Je nach dem wie ausgelastet mein Rechner ist und wie gerade im Netz verkehrt wird.

An und für sich ist das für mich Privat kein Problem. Da aber bereits mehrere Bekannte interesse angemeldet haben, möchte ich nicht, dass die Stundenlang mit der Ersteinrichtung zu tun haben.

Komplette Projekte aus Codeproject möchte ich nicht übernehmen - da gibt es bereits eine Bulkcopy Klasse für SQL CE.

Was ich wissen möchte ist, ob sich das Ganze etwas verkürzen lässt. 10 Minuten würde ich noch hinnehmen, auch als absolute Laie, da ich in dem Moment ja nicht wissen würde, was da hinter steht.

Wenn ich z.B. die Funktion raus nehme, dass bei Windows 7 der Installationsfortschritt in der Taskleiste angezeigt wird, geht es ca. 5 Minuten schneller. Aber ich weiß nicht, warum das so ist - jedenfalls versteh ich es nicht so recht.

Der Aufruf:
        private void bgwCreateRows_DoWork(object sender, DoWorkEventArgs e)
        {
            foreach (string strGetRows in clsCreateDB.createDatabaseRows())
            {
                while (strGetRows != "")
                {
                    SqlCeConnection conGetRows = new SqlCeConnection("Data Source = " + Application.StartupPath + "\\Database\\MyFinance.sdf");
                    conGetRows.Open();
                    SqlCeCommand cmdGetRows = new SqlCeCommand(strGetRows, conGetRows);
                    cmdGetRows.ExecuteNonQuery();
                    bgwCreateRows.ReportProgress(counter++);
                    int i = clsCreateDB.createDatabaseRows().Count;
                    tm.SetProgressState(TaskbarProgressBarState.Normal);
                    tm.SetProgressValue(counter,i);
                    if (counter == 1)
                    {
                        Properties.Settings.Default.dStart = DateTime.Now;
                        Properties.Settings.Default.Save();
                    }
                    else if (counter == pbRows.Maximum)
                    {
                        Properties.Settings.Default.dEnd = DateTime.Now; 
                        Properties.Settings.Default.Save();
                    }
                    conGetRows.Close();
                    TimeSpan ts = Properties.Settings.Default.dEnd - Properties.Settings.Default.dStart;
                    
                    if (counter >= pbRows.Maximum)
                    {
                        MessageBox.Show("Dauer des Imports: " + ts.ToString("hh.mm.ss"));
                    }
                }
            }
        }



Und die Methode zum Einlesen der Inserts:

   public List<string> createDatabaseRows()
        {
            string[] strFiles = Directory.GetFiles(Environment.CurrentDirectory + "\\Scripts\\Rows\\");
            List<string> lGetRows = new List<string>();
            foreach (string str in strFiles)
            {
                SqlCeConnection conGetRows = new SqlCeConnection(ConnectionString);
                StreamReader srGetRows = new StreamReader(str);
                string line;
                while ((line = srGetRows.ReadLine()) != null)
                {
                    lGetRows.Add(line);
                }

            }
            return lGetRows;
        }


Dieser Beitrag wurde von der dom bearbeitet: 10. September 2013 - 00:36

Mit allem, was du tust, machst du offenkundig, mit welcher Einstellung du durch's Leben gehst. -- Steffen Glückselig
0

Anzeige



#2 Mitglied ist offline   RalphS 

  • Gruppe: aktive Mitglieder
  • Beiträge: 8.877
  • Beigetreten: 20. Juli 07
  • Reputation: 1.126
  • Geschlecht:Männlich
  • Wohnort:Zuhause
  • Interessen:Ja

geschrieben 10. September 2013 - 04:30

Nur mal so als Schuß ins Blaue: hast Du schon mal versucht, die INSERTs in eine Transaktion zu stecken? Wenn das INSERT atomic ist, dauert das erwartungsgemäß ewig, weil ja immer erst gelesen, geschrieben und geprüft warden muß und das für jede einzelne Transaktion; wenn Du also die INSERTs zusammenfaßt und in eine steckst, könnte das was helfen.

Ansonsten...

... Gibt's ON INSERT Trigger, die lange laufen?
... Vielleicht sogar noch verschachtelt?
... Und/oder Indices?

Vielleicht liefert EXPLAIN ja was.
"If you give a man a fish he is hungry again in an hour. If you teach him to catch a fish you do him a good turn."-- Anne Isabella Thackeray Ritchie

Eingefügtes Bild
Eingefügtes Bild
0

#3 Mitglied ist offline   der dom 

  • Gruppe: aktive Mitglieder
  • Beiträge: 569
  • Beigetreten: 14. Juni 12
  • Reputation: 73
  • Geschlecht:Männlich
  • Wohnort:Zuhause
  • Interessen:Mein Haus, meine IT, Programmierung

geschrieben 10. September 2013 - 23:44

Zitat

hast Du schon mal versucht, die INSERTs in eine Transaktion zu stecken?


Nein, aber das wäre mal ein Versuch wert.

Einziges Problem was ich da sehe, das vergaß ich zu erwähnen, wäre, dass ich einen Fortschritt mit Anzeige und das auch gerne so lassen würde. Wenn es sich nicht vermeiden lässt, dann änder ich das halt.

Trigger laufen keine. Mache auch keine Anstalten in diesem Stadium welche zu basteln.
Indices in dem Fall, ähm nö. Es geht um Bankleitzahlen und Postleitzahlen die jeweils samt Ort usw. eingefügt werden sollen.

Transact Insert werde ich wie gesagt mal ausprobieren. Hoffe nur, dass es den Aufwand Wert ist das Script zu basteln. Hast du ne Idee ob ich das bestehende Script mit dem Management Studio für den SQL Server in ein Transact packen kann?

Hab das gerade nicht installiert, daher meine Frage.
Mit allem, was du tust, machst du offenkundig, mit welcher Einstellung du durch's Leben gehst. -- Steffen Glückselig
0

#4 Mitglied ist offline   RalphS 

  • Gruppe: aktive Mitglieder
  • Beiträge: 8.877
  • Beigetreten: 20. Juli 07
  • Reputation: 1.126
  • Geschlecht:Männlich
  • Wohnort:Zuhause
  • Interessen:Ja

geschrieben 11. September 2013 - 01:49

Wie das bei CE ist weiß ich leider auch nicht genau. Eigentlich mußt Du nur ein BEGIN TRANSACTION vor der ersten INSERT absetzen und dann ein COMMIT nach der letzten.

Haken daran ist natürlich: wenn das eine einzelne Transaktion ist, gehen die INSERTs nur zusammen. Schlägt eine fehl, schlagen alle fehl. Das müßte man dann gegebenenfalls mit Savepoints oder mehreren Transaktionen abfackeln, falls Erfolg nicht sichergestellt warden kann.
"If you give a man a fish he is hungry again in an hour. If you teach him to catch a fish you do him a good turn."-- Anne Isabella Thackeray Ritchie

Eingefügtes Bild
Eingefügtes Bild
0

#5 Mitglied ist offline   der dom 

  • Gruppe: aktive Mitglieder
  • Beiträge: 569
  • Beigetreten: 14. Juni 12
  • Reputation: 73
  • Geschlecht:Männlich
  • Wohnort:Zuhause
  • Interessen:Mein Haus, meine IT, Programmierung

geschrieben 11. September 2013 - 23:50

Misserfolg kann ich vom Query ansich ausschließen. Das funktioniert so.

Wie es dann im Transact aussieht, kann ich noch nicht beantworten. Werde ich gleich oder zu später Stunde mal testen. Wenn eins Fehlschlägt und der Rest auch ist es auch ok, denn ich will entweder alle oder keine Datensätze. Unvollständig sollen die Verzeichnisse nicht sein. Das wird dann, und danke für den Denkanstoß, später nochmal hinzugefügt, oder halt direkt - dass muss ich dann mal sehen. Ich habe mit Transact noch nichts gemacht daher kann ich das so auch noch gar nicht beurteilen wie der Aufwand wird.

Wichtig für mich bzw. für das spätere Ergebnis ist, dass ich wenn möglich, sämtliche Datensätze die Importiert werden auch habe und das in einer angemessenen und zu vertretenden Zeit.

Kein Mensch möchte über ne Stunde warten bis man mal so ein paar popelige Postleitzahlen und Bankleitzahlen hat.

Nötigenfalls mach ich halt, zum Thema Progressanzeige, den Step "Postleitzahlen werden importiert", dann eine Funktion die mir das Result zurück gibt, dann "Bankleitzahlen werden importiert" und das ganze dann halt als ein Marquee statt einer Fortschrittsanzeige.
Mit allem, was du tust, machst du offenkundig, mit welcher Einstellung du durch's Leben gehst. -- Steffen Glückselig
0

#6 Mitglied ist offline   RalphS 

  • Gruppe: aktive Mitglieder
  • Beiträge: 8.877
  • Beigetreten: 20. Juli 07
  • Reputation: 1.126
  • Geschlecht:Männlich
  • Wohnort:Zuhause
  • Interessen:Ja

geschrieben 12. September 2013 - 15:11

Ja, das ist auch viel zu lang.

Wenn Du Zugriff auf den Datenbankserver hast, könntest Du auch mal schauen, wie die Last dort aussieht. Wenn der so langsam ist, weil er auf dem letzten Loch pfeift, müßtest Du Dir was einfallen lassen.
"If you give a man a fish he is hungry again in an hour. If you teach him to catch a fish you do him a good turn."-- Anne Isabella Thackeray Ritchie

Eingefügtes Bild
Eingefügtes Bild
0

#7 Mitglied ist offline   der dom 

  • Gruppe: aktive Mitglieder
  • Beiträge: 569
  • Beigetreten: 14. Juni 12
  • Reputation: 73
  • Geschlecht:Männlich
  • Wohnort:Zuhause
  • Interessen:Mein Haus, meine IT, Programmierung

geschrieben 12. September 2013 - 22:28

Das Transact habe ich jetzt drin, aber ich schätze der Fehler an sich liegt einfach in der Performance des Codes an sich denn er macht nichts anderes als vorher abgesehen davon, dass er die Zeilen erst komplett schreibt, wenn alles fehlerfrei durchlief. (Hab ich doch so richtig verstanden oder?)....

Habe einen groben Schnitzer gefunden und lasse das jetzt mal durchlaufen - es geht schon um ein vielfaches schneller.

Ich habe den Fehler gemacht, die Länge der Liste jedesmal in der Schleife erneut zu zählen. Das ist bei 35k Datensätzen schon ziemlich bescheuert. Hab die Variable samt Fütterung jetzt mal aus der Schleife genommen und davor gesetzt und siehe da - es geht schneller. Jetzt schau ich mal wie da die Performance ist und ob das was ist - wenn nicht, geht´s weiter auf Fehlersuche.

Tante Edith meint: Es ist zwar gut ne halbe Stunde schneller, aber immernoch zu langsam. Noch jemand eine Idee? Sollte ich vielleicht ein Insert machen statt der zu verarbeitenden x-Tausend? Oder sollte ich die Option für Orte einfach offen lassen und nur Banken importieren womit sich das Aufkommen auf ca. 50% reduziert?

Was würdet ihr in diesem Fall als allgemein Nützlich betrachten? Wäre es gut, wenn die Orte automatisch nach Eingabe der Postleitzahl erscheinen, respektive die Postleitzahl wenn der Ort eingegeben wird?

Ich möchte natürlich die Benutzerfreundlichkeit an oberster Stelle sehen.

Dieser Beitrag wurde von der dom bearbeitet: 12. September 2013 - 23:02

Mit allem, was du tust, machst du offenkundig, mit welcher Einstellung du durch's Leben gehst. -- Steffen Glückselig
0

#8 Mitglied ist offline   RalphS 

  • Gruppe: aktive Mitglieder
  • Beiträge: 8.877
  • Beigetreten: 20. Juli 07
  • Reputation: 1.126
  • Geschlecht:Männlich
  • Wohnort:Zuhause
  • Interessen:Ja

geschrieben 13. September 2013 - 05:35

Was ich aus dem Code oben grad so ablese:

- GetFiles() dauert in Abhängigkeit von der vorhandenen Anzahl der Dateien... ewig.


- Machst Du wirklich für JEDEN Schleifendurchlauf eine neue Verbindung auf? Wenn ja: Weg damit. Stattdessen: Verbindung aufmachen, Schleife laufen lassen, Schleife zu, Verbindung zu.
Mit Schleife auf, Verbindung auf, Insert, Verbindung zu,Schleife zu geht auch die Transaction nicht durch. :wink:
Das allein beschleunigt ungemein.

- Und, aber da bin ich jetzt nur mal überflogen: Versuche, so wenig wie möglich Datenbankabfragen (pro Durchlauf) auszuführen. Die multiplizieren sich nämlich logischerweise ALLE mit der Anzahl der einzufügenden Datensätze... und jeder Zugriff, der selbst nur etwa eine Sekunde dauert, wird in der Schleife zu etwa 35000 Sekunden. Das sind stattliche 10 Stunden.
"If you give a man a fish he is hungry again in an hour. If you teach him to catch a fish you do him a good turn."-- Anne Isabella Thackeray Ritchie

Eingefügtes Bild
Eingefügtes Bild
0

#9 Mitglied ist offline   der dom 

  • Gruppe: aktive Mitglieder
  • Beiträge: 569
  • Beigetreten: 14. Juni 12
  • Reputation: 73
  • Geschlecht:Männlich
  • Wohnort:Zuhause
  • Interessen:Mein Haus, meine IT, Programmierung

geschrieben 14. September 2013 - 15:38

Den Source muss ich nochmal komplett überarbeiten, das ist so nichts. Ohne Backgroundworker_ProgressChanged für die ProgressBar läuft es im Vergleich zu vorher deutlich schneller. Ebenso natürlich mit den Verbindungen in der Schleife - was ich mir dabei Gedacht habe weiß ich nicht xD.
Mit allem, was du tust, machst du offenkundig, mit welcher Einstellung du durch's Leben gehst. -- Steffen Glückselig
0

Thema verteilen:


Seite 1 von 1

1 Besucher lesen dieses Thema
Mitglieder: 0, Gäste: 1, unsichtbare Mitglieder: 0