Évitez les doublons dans la requête INSERT INTO SELECT dans SQL Server

109

J'ai les deux tableaux suivants:

Table1
----------
ID   Name
1    A
2    B
3    C

Table2
----------
ID   Name
1    Z

J'ai besoin d'insérer des données de Table1à Table2. Je peux utiliser la syntaxe suivante:

INSERT INTO Table2(Id, Name) SELECT Id, Name FROM Table1

Cependant, dans mon cas, des identifiants en double peuvent exister dans Table2(dans mon cas, c'est juste " 1") et je ne veux pas copier à nouveau car cela générerait une erreur.

Je peux écrire quelque chose comme ça:

IF NOT EXISTS(SELECT 1 FROM Table2 WHERE Id=1)
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1 
ELSE
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1 WHERE Table1.Id<>1

Existe-t-il une meilleure façon de faire cela sans utiliser IF - ELSE? Je veux éviter deux INSERT INTO-SELECTdéclarations basées sur une condition.

Ashish Gupta
la source

Réponses:

201

Utilisation NOT EXISTS:

INSERT INTO TABLE_2
  (id, name)
SELECT t1.id,
       t1.name
  FROM TABLE_1 t1
 WHERE NOT EXISTS(SELECT id
                    FROM TABLE_2 t2
                   WHERE t2.id = t1.id)

Utilisation NOT IN:

INSERT INTO TABLE_2
  (id, name)
SELECT t1.id,
       t1.name
  FROM TABLE_1 t1
 WHERE t1.id NOT IN (SELECT id
                       FROM TABLE_2)

Utilisation LEFT JOIN/IS NULL:

INSERT INTO TABLE_2
  (id, name)
   SELECT t1.id,
          t1.name
     FROM TABLE_1 t1
LEFT JOIN TABLE_2 t2 ON t2.id = t1.id
    WHERE t2.id IS NULL

Des trois options, la LEFT JOIN/IS NULLest la moins efficace. Voir ce lien pour plus de détails .

Poneys OMG
la source
9
Juste une clarification sur la version NOT EXISTS, vous aurez besoin d'un indice WITH (HOLDLOCK) ou aucun verrou ne sera pris (car il n'y a pas de lignes à verrouiller!) Afin qu'un autre thread puisse insérer la ligne sous vous.
IDisposable
3
Intéressant, car j'ai toujours pensé que rejoindre était plus rapide que les sous-sélections. Cela concerne peut-être uniquement les jointures droites et ne s'applique pas aux jointures gauches.
Duncan
1
Duncan, la jonction est souvent plus rapide que la sous-sélection lorsqu'il s'agit de sous-requêtes corrélées. Si vous avez la sous-requête dans la liste de sélection, une jointure sera souvent plus rapide.
HLGEM
9
NOT EXISTSest particulièrement utile avec la clé primaire composite, NOT INne fonctionnera pas alors
tomash
1
@OMGPonies - votre lien pour plus de détails semble être mort. En avez-vous un autre qui pourrait être utile?
FreeMan
36

Dans MySQL, vous pouvez faire ceci:

INSERT IGNORE INTO Table2(Id, Name) SELECT Id, Name FROM Table1

SQL Server a-t-il quelque chose de similaire?

Duncan
la source
5
+1 pour m'avoir informé à ce sujet. Très belle syntaxe. Certainement plus court et meilleur que celui que j'ai utilisé. Malheureusement, le serveur SQL n'a pas cela.
Ashish Gupta
13
Pas totalement vrai. Lorsque vous créez un index unique, vous pouvez le définir sur «ignorer les doublons», auquel cas SQL Server ignorera toutes les tentatives d'ajout d'un doublon.
IamIC
2
Et SQL Server ne peut toujours pas ... pathétique.
Smack Jack
1
Donc, SQL Server ne peut toujours pas?
Ingus
8

Je viens d'avoir un problème similaire, le mot clé DISTINCT fonctionne par magie:

INSERT INTO Table2(Id, Name) SELECT DISTINCT Id, Name FROM Table1
Hunter Bingham
la source
21
À moins que je ne vous comprenne totalement, cela fonctionnera si vous avez des doublons dans l'ensemble à partir duquel vous insérez . Cependant, cela n'aidera pas si l'ensemble à partir duquel vous insérez peut être des doublons de données déjà dans le insert intotableau.
FreeMan
5

J'étais confronté au même problème récemment ...
Voici ce qui a fonctionné pour moi dans MS SQL Server 2017 ...
La clé primaire doit être définie sur l'ID dans le tableau 2 ...
Les colonnes et les propriétés de colonne doivent être les mêmes bien sûr entre les deux les tables. Cela fonctionnera la première fois que vous exécuterez le script ci-dessous. L'ID dupliqué dans le tableau 1 ne sera pas inséré ...

Si vous l'exécutez la deuxième fois, vous obtiendrez un

Violation de l'erreur de contrainte PRIMARY KEY

Voici le code:

Insert into Table_2
Select distinct *
from Table_1
where table_1.ID >1
Vishane Naicker
la source
4

L'utilisation ignore Duplicatesde l'index unique suggéré par IanC était ma solution pour un problème similaire, la création de l'index avec l'optionWITH IGNORE_DUP_KEY

In backward compatible syntax
, WITH IGNORE_DUP_KEY is equivalent to WITH IGNORE_DUP_KEY = ON.

Réf.: Index_option

Tazz602
la source
4

À partir de SQL Server, vous pouvez définir un index de clé unique sur la table pour (colonnes qui doivent être uniques)

À partir du serveur SQL, faites un clic droit sur la conception de la table, sélectionnez Index / Clés

Sélectionnez la ou les colonnes qui ne seront pas dupliquées, puis saisissez Clé unique

M. Salah
la source
1

Un peu hors sujet, mais si vous souhaitez migrer les données vers une nouvelle table, et que les doublons possibles sont dans la table d'origine , et que la colonne éventuellement dupliquée n'est pas un identifiant, ça GROUP BYfera:

INSERT INTO TABLE_2
(name)
  SELECT t1.name
  FROM TABLE_1 t1
  GROUP BY t1.name
FullStackFool
la source
-1

Un simple DELETEavant le INSERTsuffirait:

DELETE FROM Table2 WHERE Id = (SELECT Id FROM Table1)
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1

Changement Table1en Table2fonction de la table Idet de l' nameappariement que vous souhaitez conserver.

Sacro
la source
3
Veuillez ne pas faire ça. Vous dites essentiellement "quelles que soient les données que j'ai ne valent rien, insérons simplement ces nouvelles données!"
Andir
@Andir Si, pour une raison quelconque, "Table2" ne doit pas être abandonné après "INSERT", utilisez les autres méthodes, mais c'est une manière parfaitement valable de réaliser ce que l'OP a demandé.
Sacro
1
Valide, mais certainement plus lent et potentiellement corrompant sans transaction. Si vous empruntez cette voie, encapsulez une TRANSaction.
MC9000