798 Stimmen

Einfügen, bei Duplikaten aktualisieren in PostgreSQL?

Vor mehreren Monaten habe ich aus einer Antwort auf Stack Overflow gelernt, wie man mehrere Updates gleichzeitig in MySQL mit der folgenden Syntax durchführen kann:

INSERT INTO Tabelle (id, Feld, Feld2) VALUES (1, A, X), (2, B, Y), (3, C, Z)
ON DUPLICATE KEY UPDATE field=VALUES(Col1), field2=VALUES(Col2);

Jetzt bin ich zu PostgreSQL gewechselt und anscheinend ist das nicht korrekt. Es verweist auf alle richtigen Tabellen, daher nehme ich an, dass es nur darum geht, dass unterschiedliche Schlüsselwörter verwendet werden, aber ich bin mir nicht sicher, wo dies in der PostgreSQL-Dokumentation behandelt wird.

Zur Klarstellung: Ich möchte mehrere Dinge einfügen und wenn sie bereits vorhanden sind, sie aktualisieren.

46 Stimmen

Jeder, der diese Frage findet, sollte den Artikel von Depesz "Warum ist upsert so kompliziert?" lesen. Es erklärt das Problem und mögliche Lösungen sehr gut.

9 Stimmen

UPSERT wird in Postgres 9.5 hinzugefügt: wiki.postgresql.org/wiki/…

6 Stimmen

@tommed - es wurde erledigt: stackoverflow.com/a/34639631/4418

3voto

Joey Adams Punkte 39825

Bearbeiten: Dies funktioniert nicht wie erwartet. Im Gegensatz zur akzeptierten Antwort kommt es bei zwei Prozessen, die upsert_foo wiederholt gleichzeitig aufrufen, zu eindeutigen Schlüsselverletzungen.

Eureka! Ich habe einen Weg gefunden, es in einer Abfrage zu machen: Verwenden Sie UPDATE ... RETURNING, um zu testen, ob irgendwelche Zeilen betroffen waren:

CREATE TABLE foo (k INT PRIMARY KEY, v TEXT);

CREATE FUNCTION update_foo(k INT, v TEXT)
RETURNS SETOF INT AS $$
    UPDATE foo SET v = $2 WHERE k = $1 RETURNING $1
$$ LANGUAGE sql;

CREATE FUNCTION upsert_foo(k INT, v TEXT)
RETURNS VOID AS $$
    INSERT INTO foo
        SELECT $1, $2
        WHERE NOT EXISTS (SELECT update_foo($1, $2))
$$ LANGUAGE sql;

Das UPDATE muss in einer separaten Prozedur erfolgen, weil dies leider ein Syntaxfehler ist:

... WHERE NOT EXISTS (UPDATE ...)

Jetzt funktioniert es wie gewünscht:

SELECT upsert_foo(1, 'hi');
SELECT upsert_foo(1, 'bye');
SELECT upsert_foo(3, 'hi');
SELECT upsert_foo(3, 'bye');

1 Stimmen

Sie können sie in eine Anweisung kombinieren, wenn Sie einen beschreibbaren CTE verwenden. Aber wie bei den meisten hier geposteten Lösungen ist diese falsch und wird bei gleichzeitigen Updates fehlschlagen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X