SQL (Postgres) ID von Kategorietable einfügen

RedlightX

Aktiver NGBler
Registriert
18 Juli 2013
Beiträge
1.094
Hallo zusammen,
ich komme mir unheimlich dumm vor, aber ich stehe auf dem Schlauch.

Angenommen, ich habe 2 Tabellen.

1.) Produkte

- ID (Auto incr.)
- Name
- Kategorie_id


2.) Kategorien

- ID (Auto incr.)
- Kategoriename


Da es sich hierbei um eine größere CSV handelt, habe ich, bevor ich die eigentliche Produkttabelle inserte, andere Tabellen (hier: Kategorien) vorher gefüllt.
Alles klappt soweit. Die CSV lasse ich mitteln NodeJS in die Datenbank schreiben - es hapert jetzt nur noch an meinem Verständnis. Die gesamte CSV ist erstmal in einem Array. Soweit, so gut.

Nun möchte ich, nachdem alle "Nebenkriegsschauplätze" bereits mit Daten gefüllt sind, die fehlenden Produkt-IDs aus den anderen Tabellen, in die Haupt-Tabelle (Produkte) bekommen.
Da ich im Array bereits alle Daten habe, ist es aus dem ersten Blick etwas wie:

"Select id FROM Kategorien WHERE Kategoriename = array.kategoriename"

Ich möchte allerdings in die Datenbank Inserten.
Ein wenig herumgesucht habe ich und folgendes probiert:

INSERT INTO Produkte (Kategorie_id) SELECT id FROM Kategorien WHERE Kategoriename =array.kategoriename"

Das kann so nicht funktionieren, da er nicht weiss, welchem Produkt er genau diese ID in das Feld schreiben soll.

Ich stehe auf dem Schlauch.... habe ich etwas vergessen? Ich habe bereits bei der Tabellenerstellung Fremd/Primärschlüssel zugewiesen. Nützt natürlich erstmal nichts, wenn ich nicht weiss, wie ich diese genau hineinschreiben kann...


Ich wäre für Hilfe sehr sehr dankbar :T
 
War denn in der csv eine Verbindung über die IDs der Kategorie? Oder eine andere Verbindung?
Aber selbst wenn, Du legst mit Auto incr. doch wohl neue IDs für die Kategorien an.

Woher soll die Information/Zuordnung kommen?
 
  • Thread Starter Thread Starter
  • #3
In der CSV war alles nur eingeklatscht und ich möchte es natürlich auf mehrere Tabellen splitten :)

Die Zuordnung muss ich beim Import mittels nodejs selbst vornehmen. Das ist ja möglich. Da muss dann Anhand eines übereinstimmenden Namens die ID ausgegeben und eingetragen werden.
Ein wenig zum Verzweifeln, dabei wollte ich alls nur sauber geordnet haben, damit die Zugriffszeiten halbwegs stimmen...
 
Kurze Frage, ist der Produkt Kategorie Index in Tabelle 1 lückenlos durchnummeriert? Oder gibt es Lücken in dem Zähler?

Wenn du keine Lücken hast:

Um einen Wert mit fixer Id neu anzulegen/zu ergänzen:
[src=text]INSERT INTO Kategorien(ID, KategorieName) VALUES (Kategorie_id, 'kategorieName') ON CONFLICT DO NOTHING[/src]

Das bewirkt das bei einem Konflikt, wenn schon vorhanden, nichts weiter getan wird. Alternative wäre zum Beispiel explizites überschreiben, falls schon unter der "ID" ein Eintrag vorhanden ist.

Um dann den Bezug zu "ID" und "KategorieName" zu bekommen, würdest du einmalig über alle Einträge/Produkte iterieren und die ID entsprechend für die Kategorie setzen.

Wenn du dann, sagen wir "lückenlos" 100 Kategorien eingebunden hast, kannst du den Auto Increment auch noch einmal setzen, damit alle Produkte größer als 100 eingetragen werden und nichts überschreiben:
[src=text]ALTER SEQUENCE RESTART WITH 100 OWNED BY Kategorien.ID[/src]

101 ist hier der nächste Zähler im Auto-Increment.
Siehe hier:
 
Wenn ich dich richtig verstehe suchst du nach so einem Befehl: [src=postgresql]INSERT INTO Produkte (Name, Kategorie_id) VALUES ('array.produktname', (SELECT id FROM Kategorien WHERE Kategoriename = 'array.kategoriename'));[/src]Damit fügst du ein Produkt in die Liste hinzu, wobei der Kategoriename automatisch in die ID übersetzt wird, bzw [kw]NULL[/kw] in der Spalte landet, falls die Kategorie noch nicht existiert.
 
  • Thread Starter Thread Starter
  • #6
Danke, Raktorium - da kommen wir der Sache schon sehr nahe :T
Da die Grundaten - bis auf die IDs - bereits in der Tabelle Produkte vorhanden sind, habe ich es auch mit einem Update versucht

Deshalb habe ich es im Nachhinein auch mit einem Update versucht, aber ich bekomme nur einen Fehler... "error: syntax error at or near “UPDATE products” :(
Wenn ich den Befehl manuell absetze, klappt es ohne Probleme.

"UPDATE*products SET description_id=*'57' WHERE*ean = '4030152026357";

Aber theoretisch muss ich die Funktion umschreiben, und es direkt mit mehreren Subqueries lösen (wie dein Beispiel, nur etwas umfangreicher)....


Danke für eure Hilfe :T

Edit:

Ich habe die Funktion jetzt umgeschrieben, sodass er Produkt für Produkt in die DB schreibt. Allerdings kämpfe ich noch mit den Single/Doublequotes :(

[src=javascript]
pool
.query(
"INSERT INTO products (product_name, ean,description_id)
VALUES ($1,$2,$3)",
[
itemData.product_name,
itemData.ean,
'SELECT id from product_description WHERE description = "+itemData.description"'
]
)
.then(res => console.log('ok'))
.catch(err =>console.error("Error executing query", err.stack)
);
[/src]

Folgender Fehler kommt: Error executing query error: invalid input syntax for integer: "SELECT id
Ich nutze übrigens Node-Postgres. Manuelles ausführen funktioniert bereits...
 
Zuletzt bearbeitet:
Bei deiner Lösung kommt durch die Escape-Funktion wahrscheinlich so etwas heraus, da der Subselect so als String durch das Escaping nochmal mit Quotes eingesetzt wird, dadurch dann die Quotes zusätzlich an der falschen Stelle bekommt und du so gleichzeitig eine Injection-Anfälligkeit eingebaut hast.
[src=mysql]INSERT INTO products (product_name, ean,description_id) VALUES ("product_name", "ean", "SELECT id from product_description WHERE description = "description"")[/src]

Dein Code sollte daher eher so aussehen:[src=javascript]
pool
.query(
"INSERT INTO products (product_name, ean,description_id)
VALUES ($1, $2, (SELECT id from product_description WHERE description = $3 LIMIT 1))",
[
itemData.product_name,
itemData.ean,
itemData.description
]
)
.then(res => console.log('ok'))
.catch(err =>console.error("Error executing query", err.stack)
);

[/src]
 
Zuletzt bearbeitet:
  • Thread Starter Thread Starter
  • #8
Danke :T

Ich bin jetzt ebenfalls in diesem Punkt, wo ich die Query und die Values umgebaut habe:

[src=javascript]
const testquery = "INSERT INTO products (product_name, ean,description_id) VALUES ($1,$2,(SELECT id from product_description WHERE description = $3))) RETURNING *;";
const values = [
itemData.product_name,
itemData.ean,
itemData.description,
...
];
pool.query(testquery, values, (err, res) => {
if (err) console.log(err);
console.log(res.rows[0]);
});
[/src]

Es werden auch statt knapp 1k Produkte immer zwischen 4 und 11 Stück korrekt importiert, allerdings hört das Script dann auf zu arbeiten, ohne einen Fehler auszugeben. Ich gehe wirklich davon aus, dass es mit dem blöden "Textvergleich" der product_description zutun hat. Ist ja auch nich sonderlich elegant :/

Gibt es eine andere Möglichkeit die description korrekt miteinander zu vergleichen, oder wenigstens eine Fehlerausgabe zu bekommen?


Danke für eure Mühe :)
 
Naja, wenn description nicht matched, ist ja kein Fehler, daher wird es auch kein Error-Log dazu geben, die Datensätze werden dann einfach übersprungen.

Du kannst es aber einfach debuggen indem du dir die generierten SQLs ausgeben lässt und die, welche nicht funktioniert haben, genauer anschaust.
 
Wenn keine Description gefunden wird, sollte der Eintrag trotzdem eingefügt werden - mit NULL in der Spalte. Wenn die description_id natürlich auf [kw]NOT NULL[/kw] gesetzt wurde soll ein Fehler fliegen.
Hängt das Script oder stürzt es ohne Meldung ab? Und passiert das immer beim selben Produkt (lass dir das mal vor dem Query ausgeben)?

Noch einen anderen Verdacht hab ich (rein spekulativ): Du feuerst deine Querys ab, ohne auf das Ergebnis des vorherigen Befehls zu warten (die Funktion mit den logs wird ja asynchron ausgeführt). Evtl. verstopfst du damit irgendwelche Warteschlangen / blockierst Threads / läufst ins connection limit des Servers. Kannst du das so umbauen, dass jedes Query erst ausgeführt wird, wenn das vorherige abgeschlossen ist? [kw]await[/kw] dürfte das NodeJS-Schlagwort dafür sein.
 
  • Thread Starter Thread Starter
  • #11
Danke - das hilft :T
Wenn keine Description gefunden wird, kann null drin stehen. Das funktioniert auch - bzw wurden im Vorfeld Produkte ohne Description ausgeschlossen. :)

Ich habe einmal versucht, statt die description_id, die ganze description in das Feld zu schreiben, weil ich annahm, dass es da einen Fehler beim Vergleichen (Select id from produt_description WHERE description = $3). Aber daran liegt es offenbar nicht.

Der letzte Absatz macht für mich sinn! Das werde ich auf jeden Fall ausprobieren, ich muss jetzt nur sehen, wie ich es am geschicktesten einbaue.
Kleine Test-CSV mit maximal 100 Produkten funktionieren in der Regel. Also lässt das wirklich irgendwo auf die connection und Limits schließen.

Da ich immer mittels console.log() den erfolgreich eingetragenen Datensatz ausgebe, blieb es einfach still und nichts tat sich mehr. Korrekt beendet hat sich das Script nicht.


Es ist das erste Mal, dass ich etwas mit nodeJS mache und async ist komplett neu für mich. Ich hätte dafür eher PHP genommen, aber auf die Nerverei mit Timeouts etc hatte ich wenig Lust, vor allem da die CSV so groß sind...

Edit: Ja, es hört dann immer beim selben Produkt auf zu arbeiten. Aber dort ist nichts in den Datensätzen, was auf einen Fehler schliessen lässt. Wobei es durch das async ja nicht zwangsläufig Schuld genau dieser letzten Ausgabe sein muss :/
 
Was passiert denn wenn du die ersten 10 Produkte überspringst? Hängt das Script beim selben Eintrag (=> Problem mit dem Eintrag) oder nach gleich vielen Einträgen (=> Limit)?

Btw: PHP kann man wie NodeJS auch als Konsolen-Script ausführen, einfach [kw]php <dateiname>[/kw] aufrufen. Dabei gibt's auch keine Timeouts. Alternativ kann man mit den Timeout zurücksetzen.
 
  • Thread Starter Thread Starter
  • #13
Wenn ich ab einem gewissen Zähler (i = 100) etc starten wollte, oder anderweitig begrenzen wollte, tat sich garnichts.

Ich war zwischenzeitlich so frustriert, sodass ich es in PHP gelöst habe und alles läuft einwandfrei. Merkbar langsamer, aber ich habe ja heute gelernt, dass ich das Ding einfach im Terminal laufen lassen kann :D

Danke für eure Hilfe :)
 
Zurück
Oben