Ich habe diese Tabelle (erstellt von Django):
%Vor% Ich versuche, eine große Menge von Daten mit INSERT
mit einer ON CONFLICT
-Klausel einzufügen.
Der Kniff ist, dass ich die id
für alle der Zeilen abrufen muss, unabhängig davon, ob sie bereits vorhanden sind oder nicht.
In anderen Fällen würde ich etwas tun wie:
%Vor% Wenn UPDATE
ausgeführt wird, gibt die Anweisung das id
dieser Zeile zurück. Außer, es funktioniert nicht mit dieser Tabelle. Ich denke es funktioniert nicht, weil ich mehrere Felder zusammen einzigartig habe, während in anderen Fällen habe ich diese Methode verwendet, ich hatte nur ein einziges Feld.
Ich erhalte diesen Fehler, wenn ich SQL über den Django-Cursor ausführen möchte:
%Vor%
Wie mache ich die Masseneinfügung mit dieser Tabelle und bekomme die eingefügten und vorhandenen IDs zurück?
Der Fehler, den Sie bekommen:
Der BefehlON CONFLICT DO UPDATE kann die Zeile nicht ein zweites Mal beeinflussen
zeigt an, dass Sie versuchen, dieselbe Zeile mehrmals in einem einzigen Befehl hochzuladen. Mit anderen Worten: Sie haben auf (name, url, email)
in Ihrer VALUES
-Liste getäuscht. Falten Duplikate (wenn das eine Option ist) und es sollte funktionieren. Aber Sie müssen entscheiden, welche Reihe Sie aus jeder Gruppe von Betrogenen auswählen.
Da wir jetzt einen freistehenden VALUES
-Ausdruck verwenden, müssen Sie explizite Typumwandlungen für nicht standardmäßige Typen hinzufügen. Wie:
Ihre timestamptz
-Spalten benötigen einen expliziten Typ-Cast, während die String-Typen mit dem Standard text
arbeiten können. (Sie könnten dennoch sofort in varchar(n)
umwandeln.)
Es gibt Möglichkeiten, um zu bestimmen, welche Zeile aus jeder Gruppe von Duplikaten auszuwählen ist:
Sie haben Recht, es gibt (derzeit) keine Möglichkeit, ausgeschlossene Zeilen in der RETURNING
-Klausel zu erhalten. Ich zitiere das Postgres Wiki :
Beachten Sie, dass
RETURNING
den Alias "EXCLUDED.*
" nicht sichtbar macht aus demUPDATE
(nur der generische "TARGET.*
" Alias ist sichtbar Dort). Es wird angenommen, dass dies eine ärgerliche Mehrdeutigkeit für die einfache, häufige Fälle [30] für wenig bis keinen Vorteil. Bei einigen in der Zukunft zeigen, können wir einen Weg der Entlarvung verfolgen, wennRETURNING
-projizierte Tupel wurden eingefügt und aktualisiert, aber dies wahrscheinlich muss es nicht in die erste festgeschriebene Iteration von das Feature [31] .
Allerdings sollten Sie keine Zeilen aktualisieren, die nicht aktualisiert werden sollen. Leere Updates sind fast so teuer wie regelmäßige Updates - und können unbeabsichtigte Nebenwirkungen haben. Sie brauchen nicht unbedingt UPSERT, Ihr Fall sieht eher wie "SELECT oder INSERT" aus. Verwandt:
Ein saubererer Weg zum Einfügen einer Reihe von Zeilen wäre mit datenändernden CTEs:
%Vor% Die zusätzliche Komplexität sollte für große Tabellen zahlen, wobei INSERT
die Regel und SELECT
die Ausnahme ist.
Ursprünglich hatte ich ein NOT EXISTS
-Prädikat für die letzte SELECT
hinzugefügt, um Duplikate im Ergebnis zu vermeiden. Aber das war überflüssig. Alle CTEs einer einzelnen Abfrage sehen die gleichen Snapshots von Tabellen. Der mit ON CONFLICT (name, url, email) DO NOTHING
zurückgegebene Satz schließt sich gegenseitig für den Satz aus, der nach dem INNER JOIN
für die gleichen Spalten zurückgegeben wird .
Leider öffnet dies auch ein kleines Fenster für eine Race-Bedingung . Wenn ...
... einige Zeilen können verloren gehen.
Sie könnten einfach INSERT .. ON CONFLICT DO NOTHING
, gefolgt von einer separaten SELECT
Abfrage für alle Zeilen - innerhalb der gleichen Transaktion, um dies zu überwinden. Dies wiederum öffnet ein weiteres kleines Fenster für eine Race-Bedingung , wenn gleichzeitige Transaktionen Schreibvorgänge in die Tabelle zwischen INSERT
und SELECT
festschreiben können (standardmäßig READ COMMITTED
Isolationsstufe ). Kann mit REPEATABLE READ
Transaktionsisolierung vermieden werden (oder strenger ). Oder mit einer (möglicherweise teuren oder gar inakzeptablen) Schreibsperre auf dem ganzen Tisch. Sie können jedes Verhalten erhalten, das Sie brauchen, aber es kann einen Preis geben, den Sie bezahlen müssen.
Verwandte:
Tags und Links django sql postgresql upsert sql-returning