ExecuteNonQuery & Oracle

Hallo,

ich habe ein Geschwindigkeitsproblem in meiner Anwendung.

In einer foreach-Schleife gehe ich durch ein dataSet und baue mir den CommandText zusammen. Das OleDbCommand-Objekt habe ich außerhalb der foreach-Schleife definiert und ändere innerhalb der foreach-Schleife nur die Eigenschaft CommandText.

Am Ende der Schleife gebe ich den Befehl ExecuteNonQuery ab.

Die Connection ist geöffnet (da mehrere Deletes/Inserts in mehreren Methoden) und verfügt über eine Transaktion (ohne explizit definiertes IsolationLevel). Ein Commit wird erst an späterer Stelle (wieder andere Methode) durchgeführt!

Es handelt sich um insgesamt ca. 13.000 Datensätze. Durch eine Messung der einzelnen Funktionen (String zusammenbauen, Prüfung von Feldern im dataSet, etc.) habe ich herausgefunden das die Funktionen (3 Stück) insgesamt (also für alle 13.000 Datensätze) je ca. 1 Sekunde brauchen. Das ExecuteNonQuery braucht ca. 53 Sekunden.

Oracle: 8.1.5 Datenbank

Für einen Tipp wäre ich sehr dankbar, da dies mein erstes C#-Projekt ist und ich noch nicht soviel Erfahrung gesammelt habe.

Danke,
Michael

hi,

  1. was genau machst du im ExecuteNonQuery? (Welche DB Operationen mit welcher Komplexitaet…)
  2. Sind alle 13.000 ExecuteNonQueries aehnlich? (Gleiche Operation, unterschiedliche Daten?)

so nebenbei:

ich hab mal im MSDN gelesen, dass die foreach Schleife im Gegensatz zum konventionellen „for-Schleifen“ Ansatz ziemlich langsam sein soll. Leider finde ich den Artikel nicht mehr und der Unterschied waere in deinem Fall wahrscheinlich nicht zu spueren…

lg
patrick

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

vielleicht ein wenig offtopic
hallo

ich habe dein problem nicht wirklich verstanden :smile:
in einem loop solltest du jedoch nicht mit string arbeiten, sondern mit dem stringbuilder.

string ist nämmlich „immutable“, d.h. der wert der variable kann nicht geändert werden. .net erstellt dann immer wieder ein neues objekt was langsam ist :smile:

http://www.dotnetjunkies.com/Tutorial/ShowContent.as…

gruss

giuseppe

Hallo Patrick,

danke für deine Antwort.

1.) Ein reiner Insert mit 8 Feldern. Darunter eine sequence also seqence.nextval, ein sysdate, user, …

2.) Ja die Query ist sehr ähnlich. Es wird in der sequence jeweils um 1 erhöht, sysdate ist natürlich anders und die Werte auch.

Werte sind u.a.: 2003, 26, ‚M‘, 20, … (also alles int/varchar2 - nichts kompliziertes/komplexes!)

Das foreach-Schleifen langsam sein sollen (und bisweilen ungenau - sollen angeblich zum Teil Datensätze auslassen) habe ich schon gehört. Jedoch habe ich mit der Performance da kaum Probleme. Der geht die 13.000 Datensätze in 2-3 Sekunden durch, lediglich - wie schon erwähnt - dauert das ExecuteNonQuery so lange.

Hm,

Lieben Gruß
Michael

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Hallo Giuseppe,

hier noch einmal das Beispiel, ansatzweise im Code:

OleDbCommand oleDbXYZ = new OleDbCommand(sMeinString, meineConn, meineTransaktion);

(foreach dRow in dataTableXYZ)
{

[…]
// Überprüfung von Feldern im dataSet (dauert ca. 1 Sekunde für alle Datensätze)

sMeinString = string.Format(
"Insert into tabelleXYZ … ";

oleDbXYZ.CommandText = sMeinString;

oleDbXYZ.ExecuteNonQuery();
// Befehl an die Oracle-Datenbank (dauert ca. 53 Sekunden für alle Datensätze ==> Flaschenhals!!!)
}

[…]
meineTransaktion.Commit();

So ich hoffe das es nun etwas klarer wird?

Danke für die Referenz mit dem StringBuilder. Werde ich mir mal anschauen.

LG,
Michael

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Hi Michael,

hmm, falls ich das richtig verstanden habe fuerst du alle SQL Statements auf einmal aus!¿

! Moeglichweise kann ein Bulk Insert oder ein Prepared SQL Statement dein Performance Problem loesen !

Versuche einmal diesen String (also alle SQL Statements) nur in eine Textdatei zu speichern und dannach auszufueren… Moeglicherweise dauert es gleich lange… Dann liegt das Problem am Server, weil dieser nicht so viele SQL Statements so ‚schnell‘ kompilieren kann…
Du kannst auch unter Verwaltung oder eben Oracle Server spezifisch (ich hab noch nie mit Oracle gearbeitet) die SQL Statements pro Sekunde protokollieren, dann siehst du wo das Problem liegt…

Falls ich dich falsch verstanden habe, bitte um aufklaerung…

lg
patrick

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

nochmals offtopic :smile:
hat das einen bestimmten grund, wieso du die daten aus der datatable rausnimmst und sie dann via oledbCommand zu inserten?

resp. wieso arbeitest du nicht mit dem oleDBDataAdapter?

wieso das excecuteNonQuery so lange geht weiss ich leider auch nicht.

fragen:

  • was geschieht wenn du es ohne transaktion machst?
  • hast du es schon mit einem OracleCommand versucht (ist ab vstudio 2003 dabei)?

gruss

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Hallo Patrick,

ich führe die einzelnen ExecuteNonQuery nicht auf einmal aus, sondern hintereinander (13.000 mal). Zwischen jeder ExecuteNonQuery ist ein wenig Zeit, da die 3 Funktionen ja da arbeiten (siehe weiter oben mein Code-Beispiel). Da die Funktionen aber sehr schnell sind liegt der Flaschenhals - wie auch gemessen - bei dem ExecuteNonQuery.

Wie sieht ein Beispiel für Bulk Insert oder Prepared SQL Statement aus? Ich habe damit noch nicht gearbeitet …

Ferner beziehst du dich auf den Punkt „Verwaltung“, aber wo finde ich den. Im Visual Studio?
Ob es im Oracle eine Einstellung für die Anzahl der Statements je Sekunde gibt weiß ich nicht. Werde mal den DBA fragen :smile:.

Lieben Gruß,
Michael

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Hallo Giuseppe,

wieso schreibst du „offtopic“? Ich finde deine Beiträge sind hilfreich und passend?!

Wieso ich die Daten aus der DataTable rausnehme? Mache ich doch gar nicht (meine ich zumindest). Ich habe ein dataSet und gehe durch die DataTable. Da die Daten die ich ins dataSet gefüllt habe so nicht per Insert direkt ins Oracle geschrieben werden können muss ich sie prüfen und konvertieren (z.B.: int.Parse(drRow[„ID“])). Das ist bei dem Projekt leider eine Schwierigkeit. Das ich Daten erst sehr umfangreich prüfen muss, Strings abschneiden, führende Nullen trennen. Da hat jemand (vor mir) seine Hausaufgaben scheinbar nicht gemacht (Datenbankdesign!).

Wieso kein OleDbDataAdapter. Da, wie schon gerade erwähnt, die Daten umfangreich geprüft/konvertiert werden müssen. Außerdem eignet sich ExecuteNonQuery eher da es um einen reinen Befehl geht. Ich brauch keinen Rückgabewert.

Stichwort: Transaktion
Ich lasse mehrere DB-Befehle laufen. Läuft einer schief muss ich sicherstellen das ein Rollback geschieht. Die Oracle-DB hat sich teilweise aufgehängt weil das Programm abgebrochen wurde. Das verursacht unnötige Locks …

VS2003 haben wir leider noch nicht, kommt aber bestimmt demnächst. Danke für den Hinweis auf den Befehl.

LG,
Michael

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Managed Provider für Oracle
Hallo

Die Managed Provider für Oracle sind auch separat erhältlich:

http://www.microsoft.com/downloads/details.aspx?Fami…

vielleicht gehts damit wirklich schneller.
gruss

Wieso ich die Daten aus der DataTable rausnehme? Mache ich
doch gar nicht (meine ich zumindest). Ich habe ein dataSet und
gehe durch die DataTable. Da die Daten die ich ins dataSet
gefüllt habe so nicht per Insert direkt ins Oracle geschrieben
werden können muss ich sie prüfen und konvertieren (z.B.:
int.Parse(drRow[„ID“])). Das ist bei dem Projekt leider eine
Schwierigkeit. Das ich Daten erst sehr umfangreich prüfen
muss, Strings abschneiden, führende Nullen trennen. Da hat
jemand (vor mir) seine Hausaufgaben scheinbar nicht gemacht
(Datenbankdesign!).

Wieso kein OleDbDataAdapter. Da, wie schon gerade erwähnt, die
Daten umfangreich geprüft/konvertiert werden müssen. Außerdem
eignet sich ExecuteNonQuery eher da es um einen reinen Befehl
geht. Ich brauch keinen Rückgabewert.

Alles klar.

Stichwort: Transaktion
Ich lasse mehrere DB-Befehle laufen. Läuft einer schief muss
ich sicherstellen das ein Rollback geschieht. Die Oracle-DB
hat sich teilweise aufgehängt weil das Programm abgebrochen
wurde. Das verursacht unnötige Locks …

Das ist schon klar. Wollte nur wissen ob es ohne Transaktion auch noch so langsam ist… so als test.

gruss

Nun 13.000 SQL Statements zu kompilieren kann schon ein wenig dauern…

Mit Verwaltung habe ich Start->Einstellungen->Systemsteuerung->Verwaltung gemeint… Darunter dann Systemmonitor. Bei einem MS SQL Server kann man dort praktisch die SQL Statements pro Sekunde protokollieren lassen, welche der Server kompiliert… Bei einem Oracle Server muesste es auch solche Tools geben…

@Bulk Insert

Bei einem Bulk Insert fuegst du sehr viele Daten an eine Tabelle an. Da das SQL Statement ja immer gleich aussehen wuerde und sich nur die Daten aendern, gibt es die Moeglichkeit eines Bulk Insert (Ist so, als wuerde man Daten importieren…):

Am Besten du suchst im Google oder ziehst die Oracle Dokumentation zu Rate, wie ein Bulk Insert gemacht wird. Meist werden die Daten aus einer TextDatei herangezogen. Also wuerdest du im Programm diese Datei (im entsprechenden Format) erzeugen und dannach auf „einmal“ einfuegen…

@Prepared SQL Statement

Es gibt die Moeglichkeit SQL Statements am Server „vorzubereiten“, dH der Server ‚kompiliert‘ das SQL Statement und ersetzt dannach die Platzhalter mit den uebergebenen Werten…

Das wuerde bedeuten, dass du vor der Schleife das Prepared Statement aufbaust…

zB:

 strSQL = "INSERT INTO TABELLE (Feld1, Feldn) VALUES (@Wert1, @Wertn)

cmd.CommandText = strSQL;

cmd.Parameters.Add ("@Wert1",0); //Dummy Werte fuellen (Parameter anlegen)
cmd.Parameters.Add ("@Wertn","text");

cmd.Prepare(); //Veranlasst das vorkompilieren des Statements (fuehrt keine Operation aus)

foreach (...)
{
 cmd.Parameters[0].Value= intAktuellerWert //aktuelle Werte setzen
 ...

 cmd.ExecuteNonQuery(); //Ausfuehren
}

Mehr dazu in der .NET Hilfe:

System.Data.OleDb.OleDbCommand der Befehl Prepare()

Dort ist auch ein Beispiel angefuert…

Das Prepared Statement sollte auf jedenfall das Performance Problem loesen!

lg
Patrick

Hallo Patrick,

ich führe die einzelnen ExecuteNonQuery nicht auf einmal aus,
sondern hintereinander (13.000 mal). Zwischen jeder
ExecuteNonQuery ist ein wenig Zeit, da die 3 Funktionen ja da
arbeiten (siehe weiter oben mein Code-Beispiel). Da die
Funktionen aber sehr schnell sind liegt der Flaschenhals - wie
auch gemessen - bei dem ExecuteNonQuery.

Wie sieht ein Beispiel für Bulk Insert oder Prepared SQL
Statement aus? Ich habe damit noch nicht gearbeitet …

Ferner beziehst du dich auf den Punkt „Verwaltung“, aber wo
finde ich den. Im Visual Studio?
Ob es im Oracle eine Einstellung für die Anzahl der Statements
je Sekunde gibt weiß ich nicht. Werde mal den DBA fragen :smile:.

Lieben Gruß,
Michael

Danke für deine ausführliche Antwort. Ich werde mir das mal anschauen und mich ggf. hier noch einmal dazu äußern ob ich das Problem lösen konnte.

Ich hoffe doch!

LG,
Michael

[…]

Hallo Patrick,

hier nun das Ergebnis:

ca. 13.000 Datensätze werden in 26 Sekunden eingefügt. Und das wie folgt:

Nun 13.000 SQL Statements zu kompilieren kann schon ein wenig
dauern…

Mit einer Transaktion konnte ich die Zeit noch einmal drücken. Ist ja auch logisch, weil er dann die Inserts auf einmal macht und nicht nach jedem ExecuteNonQuery ein Commit.

@Bulk Insert

Habe ich nicht getestet …

@Prepared SQL Statement
[…]
Das Prepared Statement sollte auf jedenfall das Performance
Problem loesen!

Habe ich getestet, funktioniert prima! Das macht wirklich Sinn wenn man mehr als ein paar hundert Datensätze hat. Danke für den Tipp, die Doku ist auch gut zu lesen.

Anmerkung: Da ich mit dem VS 2002 arbeite (Framework 1.0.3705) habe ich die Erweiterung OracleClient noch nicht von Hause aus dabei. Kann man aber nachinstallieren (Danke Giuseppe - anderer Thread, siehe Link). Funkionalität kann ich nun benutzen, leider fehlt hierzu die Doku und die Assistenten für die OracleConnection (z.B.) fehlen auch. Kann man aber mit leben wenn man es im Code schreibt :smile:

Danke an Patrick (und Giuseppe) für die Hilfe!

LG,
Michael

Funkionalität kann ich nun

benutzen, leider fehlt hierzu die Doku und die Assistenten für
die OracleConnection (z.B.) fehlen auch. Kann man aber mit
leben wenn man es im Code schreibt :smile:

Die haben die gleichen Methoden und eigenschaften wie die OleDB’s Klassen… man kommt auch gut ohne Doku durch.

Und die Assistenten sind eh sch…se :wink:

Gruss

Danke an Patrick (und Giuseppe) für die Hilfe!

LG,
Michael