2010-03-25 12 views
73

I ont les deux tableaux suivants:éviter les doublons dans la requête INSERT INTO SELECT dans SQL Server

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

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

Je dois 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, les ID en double peuvent exister dans Table2 (dans mon cas, il est juste « 1 ») et je ne veux pas copier ce nouveau car cela lancer une erreur

Je peux écrire quelque chose comme ceci:

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 

Y at-il une meilleure façon de le faire sans utiliser IF - ELSE? Je veux éviter deux INSERT INTO-SELECT instructions basées sur certaines conditions.

Répondre

145

En utilisant 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) 

En utilisant 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) 

En utilisant 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 

Parmi les trois options, le LEFT JOIN/IS NULL est moins efficace. Voir this link for more details.

+3

Ju Pour 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

+1

Intéressant, parce que j'ai toujours cru que je devais être plus rapide que les sous-sélections. Peut-être est-ce uniquement pour les jointures droites et non pour les jointures à gauche. – 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

19

MySQL vous pouvez le faire:

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

Est-ce que SQL Server ont quelque chose de semblable?

+4

+1 pour m'éduquer à ce sujet. Très belle syntaxe. Certainement plus court et meilleur que celui que j'ai utilisé. Malheureusement, le serveur SQL ne l'a pas. –

+12

Pas tout à fait vrai. Lorsque vous créez un index unique, vous pouvez le définir sur "Ignorer les doublons", auquel cas SQL Server ignorera toute tentative d'ajout d'un doublon. – IamIC

+1

Et SQL Server ne peut toujours pas ... pathétique. –

3

En utilisant ignore Duplicates sur l'index unique as suggested by IanC here était ma solution pour un problème similaire, la création de l'index avec l'option WITH IGNORE_DUP_KEY

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

Ref .: index_option

6

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

INSERT INTO Table2(Id, Name) SELECT DISTINCT Id, Name FROM Table1 
+5

À moins que je ne vous comprenne totalement, cela fonctionnera si vous avez des doublons dans l'ensemble que vous insérez _from_. Cependant, il ne sera pas utile si l'ensemble à partir duquel vous insérez peut être des doublons de données déjà dans la table 'insert into'. – FreeMan

2

De SQL Server, vous pouvez définir un Clé unique index sur la table pour (colonnes qui doit être unique)

From sql server right click on the table design select Indexes/Keys

Select column(s) that will be not duplicate , then type Unique Key

0

Un peu hors sujet, mais si vous voulez migrer les données vers une nouvelle table, et les doublons sont possibles dans la table originale, et la colonne peut-être dupliqué n'est pas un identifiant, un GROUP BY fera:

INSERT INTO TABLE_2 
(name) 
    SELECT t1.name 
    FROM TABLE_1 t1 
    GROUP BY t1.name 
Questions connexes