Evite duplicados en la consulta INSERT INTO SELECT en SQL Server

109

Tengo las siguientes dos tablas:

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

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

Necesito insertar datos de Table1a Table2. Puedo usar la siguiente sintaxis:

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

Sin embargo, en mi caso, pueden existir ID duplicados Table2(en mi caso, es solo " 1") y no quiero copiar eso nuevamente, ya que eso arrojaría un error.

Puedo escribir algo como esto:

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 una mejor manera de hacer esto sin usar IF - ELSE? Quiero evitar dos INSERT INTO-SELECTafirmaciones basadas en alguna condición.

Ashish Gupta
fuente

Respuestas:

201

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

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

Usando 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

De las tres opciones, la LEFT JOIN/IS NULLes menos eficiente. Consulte este enlace para obtener más detalles .

Ponis dios mio
fuente
9
Solo una aclaración sobre la versión NO EXISTE, necesitará una pista WITH (HOLDLOCK) o no se tomarán bloqueos (¡porque no hay filas para bloquear!) Para que otro hilo pueda insertar la fila debajo de usted.
Disponible el
3
Interesante, porque siempre he creído que unirse es más rápido que las sub-selecciones. Quizás eso sea solo para uniones rectas y no aplicable a uniones por la izquierda.
Duncan
1
Duncan, la unión suele ser más rápida que las subselecciones cuando son subconsultas correlacionadas. Si tiene la subconsulta en la lista de selección, una combinación a menudo será más rápida.
HLGEM
9
NOT EXISTSes especialmente útil con la clave primaria compuesta, NOT INno funcionará entonces
Tomash
1
@OMGPonies: su enlace para obtener más detalles parece estar muerto. ¿Tienes otro que pueda ser de utilidad?
FreeMan
36

En MySQL puedes hacer esto:

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

¿SQL Server tiene algo similar?

Duncan
fuente
5
+1 por educarme sobre esto. Muy buena sintaxis. Definitivamente más corto y mejor que el que usé. Desafortunadamente, el servidor SQL no tiene esto.
Ashish Gupta
13
No es del todo cierto. Cuando crea un índice único, puede configurarlo para que "ignore los duplicados", en cuyo caso SQL Server ignorará cualquier intento de agregar un duplicado.
IamIC
2
Y SQL Server todavía no puede ... patético.
Smack Jack
1
¿Entonces SQL Server todavía no puede?
Ingus
8

Acabo de tener un problema similar, la palabra clave DISTINCT funciona mágicamente:

INSERT INTO Table2(Id, Name) SELECT DISTINCT Id, Name FROM Table1
Hunter Bingham
fuente
21
A menos que totalmente entienden mal que, esto va a funcionar si tiene duplicados en el conjunto que está insertando a partir . Sin embargo, no ayudará si el conjunto desde el que está insertando puede ser duplicado de datos que ya están en la insert intotabla.
FreeMan
5

Recientemente, estaba enfrentando el mismo problema ...
Esto es lo que funcionó para mí en el servidor MS SQL 2017 ...
La clave principal debe establecerse en la ID en la tabla 2 ...
Las columnas y las propiedades de las columnas deben ser las mismas, por supuesto, entre ambas mesas. Esto funcionará la primera vez que ejecute el siguiente script. El ID duplicado en la tabla 1, no se insertará ...

Si lo ejecuta por segunda vez, obtendrá un

Violación del error de restricción PRIMARY KEY

Este es el código:

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

Usar ignore Duplicatesen el índice único como lo sugirió IanC aquí fue mi solución para un problema similar, creando el índice con la OpciónWITH IGNORE_DUP_KEY

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

Ref .: index_option

Tazz602
fuente
4

Desde SQL Server, puede establecer un índice de clave única en la tabla para (columnas que deben ser únicas)

Desde el servidor SQL, haga clic derecho en el diseño de la tabla, seleccione Índices / Claves

Seleccione la (s) columna (s) que no se duplicarán, luego escriba Clave única

M. Salah
fuente
1

Un poco fuera de tema, pero si desea migrar los datos a una nueva tabla, y los posibles duplicados están en la tabla original , y la columna posiblemente duplicada no es una identificación, GROUP BYlo hará:

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

Un simple DELETEantes del INSERTsería suficiente:

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

Conmutación Table1de Table2función de la tabla de Idy nameemparejamiento desea conservar.

Sacro
fuente
3
Por favor, no hagas esto. Básicamente estás diciendo "cualquier información que tenga no tiene valor, ¡insertemos esta nueva información!"
Andir
@Andir Si por alguna razón "Table2" no debería eliminarse después de "INSERT", utilice los otros métodos, pero esta es una forma perfectamente válida de lograr lo que pidió el OP.
Sacro
1
Válido, pero ciertamente más lento y potencialmente corrupto sin una transacción. Si sigue esta ruta, envuélvase en una TRANSacción.
MC9000