PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Cosa succede con i duplicati quando si inseriscono più righe?

Il INSERT inserirà solo tutte le righe e niente accadrà speciale, a meno che hai una sorta di vincolo non consentire valori duplicati/sovrapposti (PRIMARY KEY , UNIQUE , CHECK o EXCLUDE vincolo) - che non hai menzionato nella tua domanda. Ma è di questo che probabilmente sei preoccupato.

Assumendo un UNIQUE o vincolo PK su (col1,col2) , hai a che fare con un libro di testo UPSERT situazione. Molte domande e risposte correlate da trovare qui.

In genere, se qualsiasi vincolo viene violato, viene sollevata un'eccezione che (a meno che non sia intrappolata in una sottotransazione come è possibile in un linguaggio procedurale lato server come plpgsql) eseguirà il rollback non solo dell'istruzione, ma dell'intera transazione .

Senza scritture simultanee

Ad esempio:nessun'altra transazione tenterà di scrivere nella stessa tabella contemporaneamente.

  • Escludi le righe che sono già nella tabella con WHERE NOT EXISTS ... o qualsiasi altra tecnica applicabile:

  • Seleziona le righe che non sono presenti in un'altra tabella

  • E non dimenticare di rimuovere i duplicati all'interno anche il set inserito, che non essere escluso dal semi-anti-join WHERE NOT EXISTS ...

Una tecnica per gestire entrambi contemporaneamente sarebbe 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 senza la parola chiave ALL piega le righe duplicate nell'origine. Se sai che non ci sono duplicati, o non vuoi piegare i duplicati in silenzio, usa EXCEPT ALL (o una delle altre tecniche). Vedi:

  • Utilizzo della clausola EXCEPT in PostgreSQL

In genere, se la tabella di destinazione è grande , WHERE NOT EXISTS in combinazione con DISTINCT sulla sorgente sarà probabilmente più veloce:

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
   );

Se possono esserci molti duplicati, vale la pena piegarli prima nella fonte. Altrimenti usa una sottoquery in meno.

Correlati:

  • Seleziona le righe che non sono presenti in un'altra tabella

Con scritture simultanee

Usa il UPSERT di Postgres implementazione INSERT ... ON CONFLICT ... in Postgres 9.5 o successivo:

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!

Ulteriori letture:

  • Come utilizzare RETURNING con ON CONFLICT in PostgreSQL?
  • Come faccio a inserire una riga che contiene una chiave esterna?

Documentazione:

  • Il manuale
  • La pagina del commit
  • La pagina Wiki di Postgres

Risposta di riferimento di Craig per UPSERT problemi:

  • Come eseguire l'UPSERT (MERGE, INSERT ... ON DUPLICATE UPDATE) in PostgreSQL?