mysql => Abfolge Queries

jonas3344

Neues Mitglied
Hallo zusammen,

ich komme eigentlich eher aus der PHP-Ecke, hab mich jetzt aber für ein Projekt an node gewandt.
Hintergrund ist ein Service welcher Daten aus verschiedenen, externen Quellen in eine Datenbank importiert. Da sich die Datenstruktur unterscheidet (bei mir normalisiert, sonst nicht immer, z.b. csv) müssen die Einträge in verschiedene Tabellen vorgenommen werden, abhängig vom Eintrag in der "Haupttabelle".

Eigentlich hab ich hautpsächlich ein Verständnisproblem, der tatsächliche Ablauf ist anders als ich ihn in meinem Verständnis programmiert habe.

Der Code (heruntergebrochen und vereinfacht zum Testen und nicht besonders schön):

Die Haupttabelle heisst cases, die Zusatztabelle welche die id als FK braucht add_values

Javascript:
async
    .each(rows, function (row, callback) {
        connection.beginTransaction(function (err) {
            let sql = 'INSERT INTO cases (laser) VALUES ("';
            let value = row["laser"] === undefined ? row["iol"] : row["laser"];
            sql += value + '")';
            connection.query(sql, function (error, results) {
                let insertid = results.insertId;
                let allRows = [];
                let sql = "INSERT INTO add_values(caseid, field, value) VALUES ?";
                for (el in row) {
                    let tmp = [];
                    tmp.push(results.insertId);
                    tmp.push(el);
                    tmp.push(row[el]);
                    allRows.push(tmp);
                }
                connection.query(sql, [allRows], function (error, results) {
                    connection.commit(function (err) {
                        callback();
                    });
                });
            });
        });
    })
    .then(function () {
        response.statusCode = 200;
        response.setHeader("content-type", "text/html");
        response.end("done");
    });

Mein Verständis wäre jetzt, dass mein Datenbanklog folgendes sagt:
Code:
9582 Query    START TRANSACTION
9582 Query    INSERT INTO cases (laser) VALUES ("VISX S4")
9582 Query    INSERT INTO add_values(caseid, field, value) VALUES // Alle Reihen erster Datensatz
9582 Query    COMMIT
Also das heisst alle Transactions nacheinander ausführt.

In Wahrheit habe ich aber
1. Alle START TRANSACTION
2. Alle Einträge in die Haupttabelle
3. Alle Einträge in die Nebentabelle
4. Alle COMMIT

Die mysql-Doku sagt: " Every method you invoke on a connection is queued and executed in sequence."
Das Ganze funktioniert und ist auch wirklich fix, ich hab aber Sorgen um Speicher.

Meine Fragen wären:
1) Kennt sich hier drin jemand aus?
2) Ist der Murks oben überhaupt verständlich? :)
3) Wenn jemand 1) und 2) mit ja beantworten konnte würde mich interessieren weshalb der Ablauf so ist wie er ist und weshalb das Script nicht Transaction nach Transaction abarbeitet.

Falls was unklar ist, einfach nachfragen.
 
Ich erlaube mir folgende zwei Anmerkungen, ohne wirklich Ahnung zu haben:

  1. Wenn du aus der PHP-Ecke kommst, sollte dir eigentlich bewusst sein, dass SQL-Injections und die direkte, ungefilterte Einsetzung in DB-Queries von Eingaben/Daten/Werten aus unbekannter Quelle dringend zu vermeiden sind. Denke also daran, Prepared Statements zu nutzen. Die wird es bei Node.js ja sicher auch geben.
  2. Die Methode async.each() führt deine übergegebene Funktion für jede Zeile/jeden Datensatz parallel/asynchron aus. Sprich: Daher werden die einzelnen Transactions für je einen Datensatz wohl nicht nacheinander, sondern praktisch gleichzeitig ausgeführt.
 
Ich persönlich nutze das MySQL Binding aus Node nicht.
@JR Cologne Es gibt keine Prepared Statements in node-mysql :(

Aber ohne es getestet zu haben, könnte man es vielleicht so lösen:
Javascript:
connection.beginTransaction((err) => {
    if (err) throw err;
    async
        .each(rows, (row, callback) => {
            let sql = 'INSERT INTO cases (laser) VALUES (?)';
            let value = row.laser ?? row.iol;
            connection.query(sql, [value], (err, res) => {
                if (err) throw err;
                let insertId = res.inserId;
                let allRows = [];
                let sql = 'INSERT INTO add_values (caseid, field, value) VALUES ?';
                for (let el in row) {
                    let tmp = [];
                    tmp.push(insertId);
                    tmp.push(el);
                    tmp.push(row[el]);
                    allRows.push(tmp);
                }
                connection.query(sql, [allRows], (err, res) => {
                    if (err) throw err;
                    callback();
                });
            });
        })
        .then(
            () => {
                connection.commit((err) => {
                    if (err) throw err;
                });
            },
            (err) => {
                connection.rollback();
                throw err;
            }
        );
});
 
Zuletzt bearbeitet:
@JR Cologne

1. Wie gesagt ist der Code arg heruntergebrochen. Die Feldwerte werden selbstverständlich durchgecheckt, schon beim Einlesen der Daten.

2. Das war an sich schon auch mein Gedanke. Ich würde dort aber zumindest eine leichte Durchmischung der Logeinträge erwarten und es ist halt wirklich so wie beschrieben, es ist strikt getrennt. Ich teste derzeit mit 3k Datensätzen, ich probiers mal noch mit deutlich mehr, vielleicht lässt sich das dann so bestätigen.

@asc
Vielen Dank, das Phänomen ist auch dasselbe, die Idee alles mit einer Transaction zu lösen hatte ich allerdings nicht gehabt. Das muss ich mir noch überlegen was mehr Sinn macht.
Was nutzt du dann, wenn ich fragen darf? Bzgl. Datenspeicherung bin ich offen und nicht gebunden.
 
Zurück
Oben Unten