| 

.NET C# Java Javascript Exception

4
Hallo zusammen,

ich suche eine Lösung, um mit einer Abfrage gleich mal mehrere Zeilen in eine Tabelle zu schreiben.
Man möge mir verzeihen, dass ich nicht alle Definitionen von Variablen mit schreibe; der Dim-Befehl für z. B. die Connection ist hier wohl nebensächlich. Die Datenherkunft ist ein File - der Einfachheit halber hier eine DataTable.

Mein erster Versuch mit Testdaten hatte ganz gut funktioniert:

Dim com As New MySqlCommand
com.Commandtext = "INSERT INTO tabelle(v1,v2) VALUES (0,'test'),(1,'kein test');"
com.ExecuteNonQuery()

Nun kommt es mit den echten Daten zu Problemen mit den Textfeldern. Also habe ich die Routine umgestellt auf

com.CommandText = "INSERT INTO tabelle(v1,v2) VALUES (@v1,@v2);"
com.Parameters.Add("@v1", MySqlDbType.Int64)
com.Parameters.Add("@v2", MySqlDbType.VarString)
com.Prepare()
For Each dr As DataRow in dt.Rows
com.Parameters("@v1").Value = dr.Item(0)
com.Parameters("@v2").Value = dr.Item(1)
com.ExecuteScalar()
Next

Das funktioniert auch ganz prima, allerdings dauert das bei der Anzahl der zu importierenden Daten ewig.

Daher die Frage: kann man die Version mit den Parametern auch hinbekommen, dass nicht jede Zeile einzeln, sondern mehrere Zeilen auf einmal abgeschickt werden?
23.01.2013
muffi 1,4k 1 9
muffi 1,4k 1 9
4 Antworten
1
Hier macht das jemand mit PHP, in dem er den SQL String dynamisch erzeugt:

http://stackoverflow.com/questions/4629022/how-to-insert-an-array-into-a-single-mysql-prepared-statement-w-php-and-pdo

Also wenn Du z. B. 100 Werte einfügen möchtest, dann müsstest Du in einer Schleife auch genauso oft das "(@v1,@v2)" bzw. dann "(?,?),(?,?), ...)" erzeugen. (Wobei die Daten dann auch als zweidimensionales Array übergeben werden.)

Ich kenne leider nicht die passende VB Syntax.

Hier das Beispiel, dass ich meine:
$sql = 'INSERT INTO table (memberID, programID) VALUES ';
$insertQuery = array();
$insertData = array();
// Hier werden die Arrays aufgebaut:
foreach ($data as $row) {
$insertQuery[] = '(?, ?)';
$insertData[] = $memberid;
$insertData[] = $row;
}

if (!empty($insertQuery)) {
// Hier wird an den Anfang des SQL String der Rest
// mit den ganzen (?,?), ... angefügt
$sql .= implode(', ', $insertQuery);

// Es gibt nur ein prepare ...
$stmt = $db->prepare($sql);

// ... und nur ein Execute!
$stmt->execute($insertData);
}
23.01.2013
Xantiva 2,3k 2 9
Xantiva 2,3k 2 9
PHP spreche ich überhaupt nicht. Aber für mich liest sich das (bis auf Einen) alles auch wie eine Schleife.
muffi 23.01.2013
Ich habe meine Antwort noch mal mit dem Beispiel ergänzt, was ich meinte. Natürlich hast Du eine Schleife, in der der werden aber nur die Werte "bereitgestellt". Das SQL wird nur ein Mal prepared und es gibt auch nur ein execute!
Xantiva 23.01.2013
1
Obwohl die Lösung nichts mit einer Sprache zu tun hat, die ich verstehe, gibt das den grünen Haken - denn der Klatscher auf dem Hinterkopf hat hier gezählt :-) Ich schreibe jetzt 20 Zeilen auf einmal, macht eine Zeitersparnis von 75%.
muffi 23.01.2013
Diese Vorgehensweise "INSERT...(VALUE,VALUE1), (VALUE, VALUE1)..." verursacht beim bei einem SQL-Server 2005 und älter Probleme. Wie MySql reagiert, kann ich nicht sagen. Um sicher zu gehen, wäre es ratsam, für jeden Wert in INSERT-Statement zu schreiben.
lbm1305 23.01.2013
Hmmm...blöde Tasten :-)
lbm1305 23.01.2013
@Ibm1305: Wenn für jeden Wert ein INSERT-Statement verwendet wird, dann bricht bei MySQL die Performance ganz schnell ein.
Wir verwenden mysql-dumps die alle Zeilen einer Tabelle mit nur einer INSERT-Anweisung schreiben (sehr viele Zeilen).
Das einzige Problem das wir bisher hatten war etwas wenig RAM (was sich relativ einfach lösen lässt) bei mehreren GB großen dumps.
Solange der Connector/Treiber für .NET mitspielt sollte das bei MySQL kein Problem geben.
phg 23.01.2013
@phg: Genau, es sei denn, man deaktiviert vor dem Massenimport die Indizes und aktiviert sie danach wieder. Dauert aber gefühlt länger, als wenn ich gleich mehrere Values schreibe und die Indizes lasse wie sie sind.
muffi 24.01.2013
Mit vielen Values hatte ich bei mySQL auch noch nie Probleme. Bei größerer Anzahl an Werten würde ich das allerdings auch Blockweise machen. Beispiel: 9.500 Werte => 9 Blöcke a 1.000 (geht alles mit einem Prepare) und dann noch ein Prepare für die restlichen 500. Dann gibt es auch keine RAM Probleme, wenn später irgendwer mal plötzlich die 1000fache Datenmenge verwenden möchte. ;)
Xantiva 24.01.2013
0
In .NET würde es mit einem StringBuilder funktionieren. Bei jedem Durchlauf der Schleife wird ein Append() aufgerufen.

Am Ende StringBuilder.ToString() an das CommandText Property übergeben.
Hier musst Du aber auch die Parameter-Collection des Command-Objekts verzichten.

Dim stringBuilder As StringBuilder = New StringBuilder()

For Each s As String In values
stringBuilder.AppendFormat("INSERT INTO tabelle(v1,v2) VALUES ({0}, {1});", s, s)
Next

command.CommandText = stringBuilder.ToString()
23.01.2013
lbm1305 849 1 8
lbm1305 849 1 8
Wenn ich auf Parameter verzichte, bin ich ja bei meiner Variante 1. Aufgrund von Sonderzeichen, die in den Stringfeldern vorkommen können, knallt es aber. Mit der Variante Parameter habe ich das Problem dagegen nicht.
muffi 23.01.2013
0
Stimmt. :-)
Ich weiß nicht, ob es ein Äquivalent zur SqlBulkCopy-Klasse gibt. Aber mit Hilfe der Methode WriteToServer lässt sich eine komplette DataTable wegschreiben.
23.01.2013
lbm1305 849 1 8
lbm1305 849 1 8
Wäre auch eine Variante, über den MySqlCommandBuilder zu gehen (BulkCopy habe ich im MySQL-Treiber nicht gefunden)... das check ich evtl. mal aus, dazu muss ich aber die Routine komplett umbauen. Was es mir aber wert ist, wenn dadurch die Geschwindigkeit höher wird.
muffi 23.01.2013
0
Wenn Du ohne Parameters arbeiten willst und den SQL String direkt ausführen willst, so musst Du die Strings/Textfelder quoten:
In meiner guten alten VB6 Shareware zur Synchronisierung und Sicherung von MySQL DBs namens DumpTimer, sah diese Funktion folgendermaßen aus:
sText = Replace(sText, "\", "\\")
sText = Replace(sText, Chr(34), "\" & Chr(34))
sText = Replace(sText, "'", "\'")
sText = Replace(sText, vbLf, "\n")
sText = Replace(sText, vbCr, "\r")
sText = Replace(sText, Chr(26), "\Z")
AddSlashes = Replace(sText, Chr(0), "\0")

Wie lang/groß die SQl Queries sein können, hängt von der Konfiguration des MySQl Servers ab.
Es empfiehlt sich die Daten/Queries in Chunks von X KB aufzuteilen.
Wenn man die INSERTs einzelnd schickt, dauert es deshalb länger, weil nach jedem Insert der Index neu aufgebaut wird. Zumindest war dies damals bei MySQL 3/4 der Fall.

Offtopic: Die Karavane zieht ja zur Zeit von MySQL (gekauft von SUN gekauft von Oracle) wieder zurück zu den Wurzeln (zu den Machern/Erfindern von MySQL), die damals einen Fork namens MariaDB aufgesetzt haben, der nun im Begriff ist sich durchzusetzen.
Btw.
23.01.2013
judgy 3,0k 1 1 8
Ich hatte mir (siehe Variante 1) eine ähnliche Funktion geschrieben. Aber: warum selber das Rad neu erfinden, wenn es dazu schon was gibt?
Offtopic: da hast Du Recht, aber ich habe keinen Einfluss auf die Serversoftware. MariaDB werde ich mir aber sicherlich mal privat ankucken.
muffi 24.01.2013

Stelle deine .net-Frage jetzt!