Wie schließt man ausgeschlossene Zeilen in RETURNING von INSERT ... ON CONFLICT ein

9

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?

    
Dustin Wyatt 11.03.2016, 21:04
quelle

1 Antwort

17

Der Fehler, den Sie bekommen:

  Der Befehl

ON 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.

%Vor%

Da wir jetzt einen freistehenden VALUES -Ausdruck verwenden, müssen Sie explizite Typumwandlungen für nicht standardmäßige Typen hinzufügen. Wie:

%Vor%

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 dem UPDATE (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, wenn    RETURNING -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 ...

  • Eine gleichzeitige Transaktion fügt widersprüchliche Zeilen ein
  • hat sich noch nicht verpflichtet
  • aber verpflichtet sich schließlich

... 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:

Erwin Brandstetter 12.03.2016, 03:43
quelle