PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Was passiert mit Duplikaten beim Einfügen mehrerer Zeilen?

Das INSERT fügt nur alle Zeilen und nichts ein etwas Besonderes wird passieren, es sei denn Sie haben eine Art Einschränkung doppelte/überlappende Werte nicht zulassen (PRIMARY KEY , UNIQUE , CHECK oder EXCLUDE Einschränkung) - die Sie in Ihrer Frage nicht erwähnt haben. Aber das ist es, worüber Sie sich wahrscheinlich Sorgen machen.

Angenommen ein UNIQUE oder PK-Einschränkung für (col1,col2) , haben Sie es mit einem Lehrbuch UPSERT zu tun Lage. Viele verwandte Fragen und Antworten finden Sie hier.

Im Allgemeinen, wenn irgendwelche Einschränkung verletzt wird, wird eine Ausnahme ausgelöst, die (sofern sie nicht in einer Subtransaktion gefangen ist, wie es in einer prozeduralen serverseitigen Sprache wie plpgsql möglich ist) nicht nur die Anweisung zurücksetzt, sondern die gesamte Transaktion .

Ohne gleichzeitige Schreibvorgänge

D.h.:Keine anderen Transaktionen werden gleichzeitig versuchen, in dieselbe Tabelle zu schreiben.

  • Schließen Sie Zeilen aus, die sich bereits in der Tabelle befinden, mit WHERE NOT EXISTS ... oder jede andere anwendbare Technik:

  • Zeilen auswählen, die in keiner anderen Tabelle vorhanden sind

  • Und vergessen Sie nicht, Duplikate innerhalb zu entfernen auch den eingefügten Satz, was nicht wäre durch den Semi-Anti-Join WHERE NOT EXISTS ... ausgeschlossen werden

Eine Technik, mit beiden gleichzeitig umzugehen, wäre EXCEPT :

INSERT INTO tbl (col1, col2)
VALUES
  (text 'v1', text 'v2')  -- explicit type cast may be needed in 1st row
, ('v3', 'v4')
, ('v3', 'v4')  -- beware of dupes in source
EXCEPT SELECT col1, col2 FROM tbl;

EXCEPT ohne das Schlüsselwort ALL faltet doppelte Zeilen in der Quelle. Wenn Sie wissen, dass es keine Duplikate gibt, oder Sie Duplikate nicht stillschweigend falten möchten, verwenden Sie EXCEPT ALL (oder eine der anderen Techniken). Siehe:

  • Verwenden der EXCEPT-Klausel in PostgreSQL

Im Allgemeinen, wenn die Zieltabelle groß ist , WHERE NOT EXISTS in Kombination mit DISTINCT auf der Quelle wird wahrscheinlich schneller sein:

INSERT INTO tbl (col1, col2)
SELECT *
FROM  (
   SELECT DISTINCT *
   FROM  (
       VALUES
         (text 'v1', text'v2')
       , ('v3', 'v4')
       , ('v3', 'v4')  -- dupes in source
      ) t(c1, c2)
   ) t
WHERE NOT EXISTS (
   SELECT FROM tbl
   WHERE  col1 = t.c1 AND col2 = t.c2
   );

Wenn es viele Dupes geben kann, lohnt es sich, sie zuerst in der Quelle zu folden. Verwenden Sie andernfalls eine Unterabfrage weniger.

Verwandte:

  • Wählen Sie Zeilen aus, die in keiner anderen Tabelle vorhanden sind

Mit gleichzeitigem Schreiben

Verwenden Sie das Postgres-UPSERT Implementierung INSERT ... ON CONFLICT ... in Postgres 9.5 oder später:

INSERT INTO tbl (col1,col2)
SELECT DISTINCT *  -- still can't insert the same row more than once
FROM  (
   VALUES
     (text 'v1', text 'v2')
   , ('v3','v4')
   , ('v3','v4')  -- you still need to fold dupes in source!
  ) t(c1, c2)
ON CONFLICT DO NOTHING;  -- ignores rows with *any* conflict!

Weiterführende Literatur:

  • Wie verwendet man RETURNING mit ON CONFLICT in PostgreSQL?
  • Wie füge ich eine Zeile ein, die einen Fremdschlüssel enthält?

Dokumentation:

  • Das Handbuch
  • Die Commit-Seite
  • Die Postgres-Wiki-Seite

Craigs Referenzantwort für UPSERT Probleme:

  • Wie wird UPSERT (MERGE, INSERT ... ON DUPLICATE UPDATE) in PostgreSQL durchgeführt?