¿Cómo eliminar filas duplicadas en SQL Server?

416

¿Cómo puedo eliminar filas duplicadas donde no unique row idexiste?

Mi mesa es

col1  col2 col3 col4 col5 col6 col7
john  1    1    1    1    1    1 
john  1    1    1    1    1    1
sally 2    2    2    2    2    2
sally 2    2    2    2    2    2

Quiero quedarme con lo siguiente después de la eliminación duplicada:

john  1    1    1    1    1    1
sally 2    2    2    2    2    2

He intentado algunas consultas, pero creo que dependen de tener una identificación de fila, ya que no obtengo el resultado deseado. Por ejemplo:

DELETE
FROM table
WHERE col1 IN (
    SELECT id
    FROM table
    GROUP BY id
    HAVING (COUNT(col1) > 1)
)
Miedo
fuente
55
Este no es un engaño del primer enlace. En esta pregunta no hay ID de fila y en la pregunta vinculada hay una ID de fila. Muy diferente.
Alien Technology
cambie 'SELECCIONAR ID de la tabla GRUPO POR ID que TIENE' para tener una función agregada, por ejemplo, MAX / MIN y debería funcionar.
mal estado el

Respuestas:

786

Me gustan los CTE y ROW_NUMBERcomo los dos combinados nos permiten ver qué filas se eliminan (o actualizan), por lo tanto, simplemente cambie el DELETE FROM CTE...a SELECT * FROM CTE:

WITH CTE AS(
   SELECT [col1], [col2], [col3], [col4], [col5], [col6], [col7],
       RN = ROW_NUMBER()OVER(PARTITION BY col1 ORDER BY col1)
   FROM dbo.Table1
)
DELETE FROM CTE WHERE RN > 1

DEMO (el resultado es diferente; supongo que se debe a un error tipográfico de tu parte)

COL1    COL2    COL3    COL4    COL5    COL6    COL7
john    1        1       1       1       1       1
sally   2        2       2       2       2       2

Este ejemplo determina duplicados por una sola columna col1debido a PARTITION BY col1. Si desea incluir varias columnas, simplemente agréguelas a PARTITION BY:

ROW_NUMBER()OVER(PARTITION BY Col1, Col2, ... ORDER BY OrderColumn)
Tim Schmelter
fuente
2
Gracias por una gran respuesta MSFT en contraste tiene una respuesta muy complicada aquí: stackoverflow.com/questions/18390574/…
Barka
2
@ omachu23: en este caso no importa, aunque creo que es más eficiente en el CTE que en el exterior ( AND COl1='John'). Normalmente debe aplicar el filtro en el CTE.
Tim Schmelter
1
@ omachu23: se puede utilizar cualquier SQL en el CTE (aparte de pedidos), por lo que si desea filtrar por Johns: ...FROM dbo.Table1 WHERE Col1='John'. Aquí está el violín: sqlfiddle.com/#!6/fae73/744/0
Tim Schmelter
1
La solución más fácil puede ser la set rowcount 1 delete from t1 where col1=1 and col2=1que se ve aquí
Zorgarath
15
Esta respuesta solo eliminará las filas que tienen duplicados en col1. Agregue las columnas en "select" a "partición por", por ejemplo, usando select en la respuesta: RN = ROW_NUMBER () OVER (PARTICIÓN POR col1, col2, col3, col4, col5, col6, col7 ORDEN POR col1)
rlee
159

Preferiría CTE para eliminar filas duplicadas de la tabla del servidor sql

recomiendo seguir este artículo :: http://codaffection.com/sql-server-article/delete-duplicate-rows-in-sql-server/

manteniendo original

WITH CTE AS
(
SELECT *,ROW_NUMBER() OVER (PARTITION BY col1,col2,col3 ORDER BY col1,col2,col3) AS RN
FROM MyTable
)

DELETE FROM CTE WHERE RN<>1

sin guardar original

WITH CTE AS
(SELECT *,R=RANK() OVER (ORDER BY col1,col2,col3)
FROM MyTable)
 
DELETE CTE
WHERE R IN (SELECT R FROM CTE GROUP BY R HAVING COUNT(*)>1)
Shamseer K
fuente
2
La función de ventanas es una gran solución.
Robert Casey
2
Estoy un poco confundido Lo eliminó de CTE, no de la tabla original. ¿Entonces, cómo funciona?
Bigeyes
8
@Bigeyes eliminar registros de CTE eliminará los registros correspondientes de la tabla física real (porque CTE contiene referencias a registros reales).
Shamseer K
No tenía idea de que este era el caso hasta esta publicación ... Gracias
Zakk Díaz
1
¿Por qué querrías eliminar tanto el original como su duplicado? No entiendo por qué no querrías simplemente eliminar el duplicado y quedarte con el otro.
Rico
52

Sin usar CTEy ROW_NUMBER()solo puede eliminar los registros simplemente usando group by con la MAXfunción aquí es y ejemplo

DELETE
FROM MyDuplicateTable
WHERE ID NOT IN
(
SELECT MAX(ID)
FROM MyDuplicateTable
GROUP BY DuplicateColumn1, DuplicateColumn2, DuplicateColumn3)
Aamir
fuente
44
Esta consulta eliminará los registros no duplicados.
Derek Smalls
8
Esto funciona bien, gracias. @DerekSmalls esto no elimina mis registros no duplicados.
monteirobrena
1
O puede mantener los registros originales usandoMIN(ID)
Savage
18
DELETE from search
where id not in (
   select min(id) from search
   group by url
   having count(*)=1

   union

   SELECT min(id) FROM search
   group by url
   having count(*) > 1
)
Shoja Hamid
fuente
¿No podría volver a escribir en: donde id en (seleccione max (id) ... teniendo count (*)> 1)?
Brent
1
No creo que haya necesidad de usar having o union, esto será suficiente: eliminar de la búsqueda donde no está la identificación (seleccione min (id) del grupo de búsqueda por url)
Christopher Yang
9

Por favor, consulte la siguiente forma de eliminación también.

Declare @table table
(col1 varchar(10),col2 int,col3 int, col4 int, col5 int, col6 int, col7 int)
Insert into @table values 
('john',1,1,1,1,1,1),
('john',1,1,1,1,1,1),
('sally',2,2,2,2,2,2),
('sally',2,2,2,2,2,2)

Creó una tabla de muestra llamada @tabley la cargó con datos dados

ingrese la descripción de la imagen aquí

Delete  aliasName from (
Select  *,
        ROW_NUMBER() over (Partition by col1,col2,col3,col4,col5,col6,col7 order by col1) as rowNumber
From    @table) aliasName 
Where   rowNumber > 1

Select * from @table

ingrese la descripción de la imagen aquí

Nota: Si está dando todas las columnas en la Partition byparte, entonces order byno tiene mucha importancia.

Lo sé, la pregunta se hizo hace tres años, y mi respuesta es otra versión de lo que Tim ha publicado, pero publicar solo en caso de que sea útil para cualquiera.

Jithin Shaji
fuente
9

Si no tiene referencias, como claves foráneas, puede hacerlo. Lo hago mucho cuando pruebo pruebas de concepto y los datos de prueba se duplican.

SELECT DISTINCT [col1],[col2],[col3],[col4],[col5],[col6],[col7]

INTO [newTable]

Entra en el explorador de objetos y elimina la tabla anterior.

Cambie el nombre de la nueva tabla con el nombre de la tabla anterior.

Rhys
fuente
Esta es la forma más sencilla que aprendí en mis materiales de introducción y que uso.
Eric
7

Microsoft tiene una guía muy ordenada sobre cómo eliminar duplicados. Echa un vistazo a http://support.microsoft.com/kb/139444

En resumen, esta es la forma más fácil de eliminar duplicados cuando solo tiene algunas filas para eliminar:

SET rowcount 1;
DELETE FROM t1 WHERE myprimarykey=1;

myprimarykey es el identificador de la fila.

Puedo establecer recuento de filas a 1 porque sólo tenía dos filas que se duplicaron. Si hubiera tenido 3 filas duplicadas entonces tendría conjunto recuento de filas a 2 para que elimine los dos primeros que se ve y sólo deja a uno en la tabla t1.

Espero que ayude a cualquiera

oabarca
fuente
1
¿Cómo sé cuántas filas he duplicado si tengo 10k filas?
Fearghal
@Fearghal intente "seleccionar primaryKey, contar (*) del grupo myTable por primaryKey;"
oabarca
1
Pero, ¿qué pasa si hay un número variable de filas duplicadas? es decir, la fila a tiene 2 registros y la fila b tiene 5 registros y la fila c no tiene registros duplicados
termita
1
@ user2070775 ¿Qué sucede si solo un subconjunto de todas las filas tiene duplicados, y de esos duplicados, algunos se duplican dos veces y otros tres o cuatro veces?
termita el
@ user2070775 Me perdí la parte en la que dijiste "solo unas pocas filas para eliminar". También hay un aviso en la página sobre conjunto recuento de filas que en futuras versiones de SQL no lo puedo afecta declaraciones actualizar o borrar
termita
6

Tratar de usar:

SELECT linkorder
    ,Row_Number() OVER (
        PARTITION BY linkorder ORDER BY linkorder DESC
        ) AS RowNum
FROM u_links

ingrese la descripción de la imagen aquí

Fezal halai
fuente
4

Después de probar la solución sugerida anteriormente, eso funciona para tablas medianas pequeñas. Puedo sugerir esa solución para tablas muy grandes. ya que se ejecuta en iteraciones.

  1. Descarte todas las vistas de dependencia del LargeSourceTable
  2. puede encontrar las dependencias utilizando sql managment studio, haga clic con el botón derecho en la tabla y haga clic en "Ver dependencias"
  3. Renombrar la tabla:
  4. sp_rename 'LargeSourceTable', 'LargeSourceTable_Temp'; GO
  5. Cree de LargeSourceTablenuevo, pero ahora, agregue una clave principal con todas las columnas que definen las duplicaciones.WITH (IGNORE_DUP_KEY = ON)
  6. Por ejemplo:

    CREATE TABLE [dbo].[LargeSourceTable] ( ID int IDENTITY(1,1), [CreateDate] DATETIME CONSTRAINT [DF_LargeSourceTable_CreateDate] DEFAULT (getdate()) NOT NULL, [Column1] CHAR (36) NOT NULL, [Column2] NVARCHAR (100) NOT NULL, [Column3] CHAR (36) NOT NULL, PRIMARY KEY (Column1, Column2) WITH (IGNORE_DUP_KEY = ON) ); GO

  7. Cree de nuevo las vistas que dejó caer en primer lugar para la nueva tabla creada

  8. Ahora, ejecute el siguiente script sql, verá los resultados en 1,000,000 de filas por página, puede cambiar el número de fila por página para ver los resultados con más frecuencia.

  9. Tenga en cuenta que configuré la IDENTITY_INSERTactivación y desactivación porque una de las columnas contiene una identificación incremental automática, que también estoy copiando

SET IDENTITY_INSERT LargeSourceTable ON DECLARE @PageNumber AS INT, @RowspPage AS INT DECLARE @TotalRows AS INT declare @dt varchar(19) SET @PageNumber = 0 SET @RowspPage = 1000000 select @TotalRows = count (*) from LargeSourceTable_TEMP

While ((@PageNumber - 1) * @RowspPage < @TotalRows )
Begin
    begin transaction tran_inner
        ; with cte as
        (
            SELECT * FROM LargeSourceTable_TEMP ORDER BY ID
            OFFSET ((@PageNumber) * @RowspPage) ROWS
            FETCH NEXT @RowspPage ROWS ONLY
        )

        INSERT INTO LargeSourceTable 
        (
             ID                     
            ,[CreateDate]       
            ,[Column1]   
            ,[Column2] 
            ,[Column3]       
        )       
        select 
             ID                     
            ,[CreateDate]       
            ,[Column1]   
            ,[Column2] 
            ,[Column3]       
        from cte

    commit transaction tran_inner

    PRINT 'Page: ' + convert(varchar(10), @PageNumber)
    PRINT 'Transfered: ' + convert(varchar(20), @PageNumber * @RowspPage)
    PRINT 'Of: ' + convert(varchar(20), @TotalRows)

    SELECT @dt = convert(varchar(19), getdate(), 121)
    RAISERROR('Inserted on: %s', 0, 1, @dt) WITH NOWAIT
    SET @PageNumber = @PageNumber + 1
End

SET IDENTITY_INSERT LargeSourceTable OFF

Moshe Taieb
fuente
4

Hay dos soluciones en mysql:

A) Eliminar filas duplicadas usando la DELETE JOINinstrucción

DELETE t1 FROM contacts t1
INNER JOIN contacts t2 
WHERE 
    t1.id < t2.id AND 
    t1.email = t2.email;

Esta consulta hace referencia a la tabla de contactos dos veces, por lo tanto, utiliza el alias de la tabla t1y t2.

El resultado es:

1 consulta OK, 4 filas afectadas (0,10 segundos)

En caso de que desee eliminar filas duplicadas y conservar lowest id, puede usar la siguiente instrucción:

DELETE c1 FROM contacts c1
INNER JOIN contacts c2 
WHERE
    c1.id > c2.id AND 
    c1.email = c2.email;

   

B) Eliminar filas duplicadas usando una tabla intermedia

A continuación se muestran los pasos para eliminar filas duplicadas utilizando una tabla intermedia:

    1. Cree una nueva tabla con la estructura igual a la tabla original que desea eliminar filas duplicadas.

    2. Inserte filas distintas de la tabla original a la tabla inmediata.

    3. Inserte filas distintas de la tabla original a la tabla inmediata.

 

Paso 1. Cree una nueva tabla cuya estructura sea la misma que la tabla original:

CREATE TABLE source_copy LIKE source;

Paso 2. Inserte filas distintas de la tabla original a la nueva tabla:

INSERT INTO source_copy
SELECT * FROM source
GROUP BY col; -- column that has duplicate values

Paso 3. suelte la tabla original y cambie el nombre de la tabla inmediata a la original

DROP TABLE source;
ALTER TABLE source_copy RENAME TO source;

Fuente: http://www.mysqltutorial.org/mysql-delete-duplicate-rows/

Bashirpour
fuente
2
-- this query will keep only one instance of a duplicate record.
;WITH cte
     AS (SELECT ROW_NUMBER() OVER (PARTITION BY col1, col2, col3-- based on what? --can be multiple columns
                                       ORDER BY ( SELECT 0)) RN
         FROM   Mytable)



delete  FROM cte
WHERE  RN > 1
Hasan Shouman
fuente
2

Debe agrupar por registros duplicados de acuerdo con los campos, luego mantener uno de los registros y eliminar el resto. Por ejemplo:

DELETE prg.Person WHERE Id IN (
SELECT dublicateRow.Id FROM
(
select MIN(Id) MinId, NationalCode
 from  prg.Person group by NationalCode  having count(NationalCode ) > 1
 ) GroupSelect
 JOIN  prg.Person dublicateRow ON dublicateRow.NationalCode = GroupSelect.NationalCode 
 WHERE dublicateRow.Id <> GroupSelect.MinId)
Hadi Salehy
fuente
2

Eliminar duplicados de una tabla enorme (varios millones de registros) puede llevar mucho tiempo. Le sugiero que haga una inserción masiva en una tabla temporal de las filas seleccionadas en lugar de eliminar.

--REWRITING YOUR CODE(TAKE NOTE OF THE 3RD LINE) WITH CTE AS(SELECT NAME,ROW_NUMBER() 
OVER (PARTITION BY NAME ORDER BY NAME) ID FROM @TB) SELECT * INTO #unique_records FROM 
CTE WHERE ID =1;
Emmanuel Bull
fuente
2

Se puede hacer de muchas maneras en el servidor sql. La forma más sencilla de hacerlo es: insertar las filas distintas de la tabla de filas duplicadas en la nueva tabla temporal. Luego elimine todos los datos de la tabla de filas duplicadas y luego inserte todos los datos de la tabla temporal que no tiene duplicados como se muestra a continuación.

select distinct * into #tmp From table
   delete from table
   insert into table
   select * from #tmp drop table #tmp

   select * from table

Eliminar filas duplicadas usando la expresión de tabla común (CTE)

With CTE_Duplicates as 
(select id,name , row_number() 
over(partition by id,name order by id,name ) rownumber  from table  ) 
delete from CTE_Duplicates where rownumber!=1
Md Masududzaman Khan
fuente
1
with myCTE
as

(
select productName,ROW_NUMBER() over(PARTITION BY productName order by slno) as Duplicate from productDetails
)
Delete from myCTE where Duplicate>1
Debendra Dash
fuente
1

Con referencia a https://support.microsoft.com/en-us/help/139444/how-to-remove-duplicate-rows-from-a-table-in-sql-server

La idea de eliminar duplicados implica

  • a) Proteger esas filas que no están duplicadas
  • b) Conserve una de las muchas filas que calificaron juntas como duplicadas.

Paso a paso

  • 1) Primero identifique las filas que satisfacen la definición de duplicado e insértelas en la tabla temporal, digamos #tableAll.
  • 2) Seleccione filas no duplicadas (filas simples) o distintas en la tabla temporal, digamos #tableUnique.
  • 3) Eliminar de la tabla fuente uniendo #tableAll para eliminar los duplicados.
  • 4) Inserte en la tabla de origen todas las filas de #tableUnique.
  • 5) Suelte #tableAll y #tableUnique
rajibdotnet
fuente
1

Si tiene la capacidad de agregar una columna a la tabla temporalmente, esta fue una solución que funcionó para mí:

ALTER TABLE dbo.DUPPEDTABLE ADD RowID INT NOT NULL IDENTITY(1,1)

Luego realice un BORRAR usando una combinación de MIN y GROUP BY

DELETE b
FROM dbo.DUPPEDTABLE b
WHERE b.RowID NOT IN (
                     SELECT MIN(RowID) AS RowID
                     FROM dbo.DUPPEDTABLE a WITH (NOLOCK)
                     GROUP BY a.ITEM_NUMBER,
                              a.CHARACTERISTIC,
                              a.INTVALUE,
                              a.FLOATVALUE,
                              a.STRINGVALUE
                 );

Verifique que DELETE haya funcionado correctamente:

SELECT a.ITEM_NUMBER,
    a.CHARACTERISTIC,
    a.INTVALUE,
    a.FLOATVALUE,
    a.STRINGVALUE, COUNT(*)--MIN(RowID) AS RowID
FROM dbo.DUPPEDTABLE a WITH (NOLOCK)
GROUP BY a.ITEM_NUMBER,
    a.CHARACTERISTIC,
    a.INTVALUE,
    a.FLOATVALUE,
    a.STRINGVALUE
ORDER BY COUNT(*) DESC 

El resultado no debe tener filas con un recuento mayor que 1. Finalmente, elimine la columna rowid:

ALTER TABLE dbo.DUPPEDTABLE DROP COLUMN RowID;
j.casco
fuente
0

Otra forma de eliminar filas duplicadas sin perder información en un solo paso es la siguiente:

delete from dublicated_table t1 (nolock)
join (
    select t2.dublicated_field
    , min(len(t2.field_kept)) as min_field_kept
    from dublicated_table t2 (nolock)
    group by t2.dublicated_field having COUNT(*)>1
) t3 
on t1.dublicated_field=t3.dublicated_field 
    and len(t1.field_kept)=t3.min_field_kept
Tolga Gölelçin
fuente
0

Oh wow, me siento tan estúpido al preparar todas estas respuestas, son como la respuesta de los expertos con todo el CTE y la tabla temporal, etc.

Y todo lo que hice para que funcionara fue simplemente agregar la columna ID usando MAX.

DELETE FROM table WHERE col1 IN (
    SELECT MAX(id) FROM table GROUP BY id HAVING ( COUNT(col1) > 1 )
)

NOTA: es posible que deba ejecutarlo varias veces para eliminar duplicados, ya que esto solo eliminará un conjunto de filas duplicadas a la vez.

hecho un desastre
fuente
Esto no funcionará ya que eliminará todos los duplicados sin dejar los originales. OP está pidiendo preservar los registros originales.
0xdd
2
No es cierto, max le dará una ID máxima que cumpla con las condiciones. Si eso no es cierto, pruebe su caso de voto negativo.
mal estado el
0
DECLARE @TB TABLE(NAME VARCHAR(100));
INSERT INTO @TB VALUES ('Red'),('Red'),('Green'),('Blue'),('White'),('White')
--**Delete by Rank**
;WITH CTE AS(SELECT NAME,DENSE_RANK() OVER (PARTITION BY NAME ORDER BY NEWID()) ID FROM @TB)
DELETE FROM CTE WHERE ID>1
SELECT NAME FROM @TB;
--**Delete by Row Number** 
;WITH CTE AS(SELECT NAME,ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY NAME) ID FROM @TB)
DELETE FROM CTE WHERE ID>1;
SELECT NAME FROM @TB;
Surinder Singh
fuente
Eliminar duplicados de una tabla enorme (varios millones de registros) puede llevar mucho tiempo. Le sugiero que haga una inserción masiva en una tabla temporal de las filas seleccionadas en lugar de eliminar. '--ESCRIBIR SU CÓDIGO (TOMA NOTA DE LA 3ª LÍNEA) CON CTE COMO (SELECCIONE NOMBRE, ROW_NUMBER () SOBRE (PARTICIÓN POR NOMBRE ORDEN POR NOMBRE) ID DE @TB) SELECCIONE * EN #unique_records DESDE CTE DONDE ID = 1; '
Emmanuel Bull
0
DELETE FROM TBL1  WHERE ID  IN
(SELECT ID FROM TBL1  a WHERE ID!=
(select MAX(ID) from TBL1  where DUPVAL=a.DUPVAL 
group by DUPVAL
having count(DUPVAL)>1))
Dr.Stark
fuente