2012-07-23 10 views
10

I asked this question a while back para eliminar registros duplicados en función de una columna. La respuesta funcionó muy bien:¿Puedo eliminar duplicados de bases de datos basados ​​en múltiples columnas?

delete from tbl 
where id NOT in 
(
select min(id) 
from tbl 
group by sourceid 
) 

Ahora tengo una situación similar, pero la definición de registro duplicado se basa en varias columnas. ¿Cómo puedo alterar este SQL anterior para identificar registros duplicados donde un registro único se define como concatenado de Col1 + Col2 + Col3. ¿Haría algo como esto?

delete from tbl 
where id NOT in 
(
select min(id) 
from tbl 
group by col1, col2, col3 
) 
+0

El principio sigue en pie: no importa si la agrupación es por una o varias columnas. Vas a retener solo la primera fila de un grupo. Pero, si no le importa que lo diga, asegúrese de verificar sus datos antes de emitir la eliminación. Convierta eliminar para seleccionar y ver qué no sobrevivirá. –

+0

@Nikola Markovinovic - parece que está haciendo lo correcto, pero solo quería confirmar – leora

+0

@leora La sintaxis que utiliza es engorrosa para conceptualizar/aplicar ingeniería inversa. Y no se traduce bien en los casos en que la identificación puede ser nula (ya que 'no en 'no se comporta como cabría esperar cuando se trata de nulos). Me di cuenta de que no es probable que sea un factor aquí, pero es importante aprender sobre los CTE y NO EXISTE para los casos en que podría ... –

Respuesta

23

Esta muestra las filas que desea mantener:

;WITH x AS 
(
    SELECT col1, col2, col3, rn = ROW_NUMBER() OVER 
     (PARTITION BY col1, col2, col3 ORDER BY id) 
    FROM dbo.tbl 
) 
SELECT col1, col2, col3 FROM x WHERE rn = 1; 

Esto muestra las filas que desea eliminar:

;WITH x AS 
(
    SELECT col1, col2, col3, rn = ROW_NUMBER() OVER 
     (PARTITION BY col1, col2, col3 ORDER BY id) 
    FROM dbo.tbl 
) 
SELECT col1, col2, col3 FROM x WHERE rn > 1; 

Y una vez que estás feliz de que los dos juegos anteriores son correctos, el siguiente en realidad los eliminará:

;WITH x AS 
(
    SELECT col1, col2, col3, rn = ROW_NUMBER() OVER 
     (PARTITION BY col1, col2, col3 ORDER BY id) 
    FROM dbo.tbl 
) 
DELETE x WHERE rn > 1; 

Tenga en cuenta que en las tres consultas, las primeras 6 líneas son idénticas, y solo la consulta posterior después del CTE ha cambiado.

+0

¡Gran solución Aaron! – mark1234

+0

Excelente solución. – CheGuevarasBeret

4

Pruebe este. Creé una tabla tblA con tres columnas.

CREATE TABLE tblA 
(
id int IDENTITY(1, 1), 
colA int, 
colB int, 
colC int 
) 

Y se han agregado algunos valores duplicados.

INSERT INTO tblA VALUES (1, 2, 3) 
INSERT INTO tblA VALUES (1, 2, 3) 
INSERT INTO tblA VALUES (4, 5, 6) 
INSERT INTO tblA VALUES (7, 8, 9) 
INSERT INTO tblA VALUES (7, 8, 9) 

Si reemplaza la selección con una eliminación en la siguiente declaración, tendrá su trabajo de eliminación de columna múltiple.

SELECT MIN(Id) as id 
FROM 
(
SELECT COUNT(*) as aantal, a.colA, a.colB, a.colC 
FROM tblA  a 
INNER JOIN tblA b ON b.ColA = a.ColA 
        AND b.ColB = a.ColB 
        AND b.ColC = a.ColC 
GROUP BY a.id, a.colA, a.colB, a.colC 
HAVING COUNT(*) > 1 
) c 
INNER JOIN tblA d ON d.ColA = c.ColA 
        AND d.ColB = c.ColB 
        AND d.ColC = c.ColC 
GROUP BY d.colA, d.colB, d.colC 
+1

El "triángulo self-join" * funciona * pero es tan torpe ... – ErikE

Cuestiones relacionadas