PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Que se passe-t-il avec les doublons lors de l'insertion de plusieurs lignes ?

Le INSERT va simplement insérer toutes les lignes et rien spécial se produira, à moins que vous avez une sorte de contrainte interdire les valeurs en double/qui se chevauchent (PRIMARY KEY , UNIQUE , CHECK ou EXCLUDE contrainte) - que vous n'avez pas mentionné dans votre question. Mais c'est probablement ce qui vous inquiète.

En supposant un UNIQUE ou contrainte PK sur (col1,col2) , vous avez affaire à un manuel UPSERT situation. De nombreuses questions et réponses connexes à trouver ici.

Généralement, si tout contrainte est violée, une exception est levée qui (à moins qu'elle ne soit piégée dans une sous-transaction comme c'est possible dans un langage procédural côté serveur comme plpgsql) annulera non seulement la déclaration, mais la transaction entière .

Sans écritures simultanées

C'est-à-dire :aucune autre transaction n'essaiera d'écrire dans la même table en même temps.

  • Exclure les lignes qui sont déjà dans le tableau avec WHERE NOT EXISTS ... ou toute autre technique applicable :

  • Sélectionnez les lignes qui ne sont pas présentes dans un autre tableau

  • Et n'oubliez pas de supprimer les doublons dans l'ensemble inséré également, ce qui ne serait pas être exclu par la semi-anti-jointure WHERE NOT EXISTS ...

Une technique pour gérer les deux à la fois serait 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 sans le mot clé ALL plie les lignes en double dans la source. Si vous savez qu'il n'y a pas de doublons, ou si vous ne voulez pas plier les doublons en silence, utilisez EXCEPT ALL (ou l'une des autres techniques). Voir :

  • Utilisation de la clause EXCEPT dans PostgreSQL

Généralement, si la table cible est grande , WHERE NOT EXISTS en combinaison avec DISTINCT sur la source sera probablement plus rapide :

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

S'il peut y avoir de nombreuses dupes, il est avantageux de les intégrer d'abord à la source. Sinon, utilisez une sous-requête de moins.

Connexe :

  • Sélectionnez les lignes qui ne sont pas présentes dans un autre tableau

Avec écritures simultanées

Utilisez Postgres UPSERT implémentation INSERT ... ON CONFLICT ... dans Postgres 9.5 ou plus tard :

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!

Lectures complémentaires :

  • Comment utiliser RETURNING avec ON CONFLICT dans PostgreSQL ?
  • Comment insérer une ligne contenant une clé étrangère ?

Documents :

  • Le manuel
  • La page de validation
  • La page Wiki de Postgres

Réponse de référence de Craig pour UPSERT problèmes :

  • Comment UPSERT (MERGE, INSERT ... ON DUPLICATE UPDATE) dans PostgreSQL ?