Pregunta

¿Cuál es la mejor manera de eliminar filas duplicadas de un archivo bastante grande? SQL Server mesa (es decir¿Más de 300.000 filas)?

Las filas, por supuesto, no serán duplicados perfectos debido a la existencia del RowID campo de identidad.

Mi mesa

RowID int not null identity(1,1) primary key,
Col1 varchar(20) not null,
Col2 varchar(2048) not null,
Col3 tinyint not null
¿Fue útil?

Solución

Suponiendo que no haya valores nulos, usted GROUP BY las columnas únicas, y SELECT el MIN (or MAX) RowId como la fila a conservar.Luego, simplemente elimine todo lo que no tenga una identificación de fila:

DELETE FROM MyTable
LEFT OUTER JOIN (
   SELECT MIN(RowId) as RowId, Col1, Col2, Col3 
   FROM MyTable 
   GROUP BY Col1, Col2, Col3
) as KeepRows ON
   MyTable.RowId = KeepRows.RowId
WHERE
   KeepRows.RowId IS NULL

En caso de que tenga un GUID en lugar de un número entero, puede reemplazar

MIN(RowId)

con

CONVERT(uniqueidentifier, MIN(CONVERT(char(36), MyGuidColumn)))

Otros consejos

Otra posible forma de hacer esto es

; 

--Ensure that any immediately preceding statement is terminated with a semicolon above
WITH cte
     AS (SELECT ROW_NUMBER() OVER (PARTITION BY Col1, Col2, Col3 
                                       ORDER BY ( SELECT 0)) RN
         FROM   #MyTable)
DELETE FROM cte
WHERE  RN > 1;

estoy usando ORDER BY (SELECT 0) arriba ya que es arbitrario qué fila preservar en caso de empate.

Para preservar el último en RowID orden, por ejemplo, podrías usar ORDER BY RowID DESC

Planes de ejecución

El plan de ejecución para esto suele ser más simple y eficiente que el de la respuesta aceptada, ya que no requiere la autounión.

Execution Plans

Esto no es siempre el caso, sin embargo.Un lugar donde el GROUP BY La solución que podría preferirse son aquellas situaciones en las que agregado de hash se elegiría con preferencia a un agregado de flujo.

El ROW_NUMBER La solución siempre dará prácticamente el mismo plan, mientras que la GROUP BY La estrategia es más flexible.

Execution Plans

Los factores que podrían favorecer el enfoque del agregado de hash serían

  • No hay índice útil en las columnas de partición
  • relativamente menos grupos con relativamente más duplicados en cada grupo

En versiones extremas de este segundo caso (si hay muy pocos grupos con muchos duplicados en cada uno), también se podría considerar simplemente insertar las filas para mantener en una nueva tabla y luego TRUNCATE-copiar el original y copiarlo nuevamente para minimizar el registro en comparación con eliminar una proporción muy alta de filas.

Hay un buen artículo sobre eliminando duplicados en el sitio de soporte de Microsoft.Es bastante conservador (te piden que hagas todo en pasos separados) pero debería funcionar bien en mesas grandes.

He usado autouniones para hacer esto en el pasado, aunque probablemente podría mejorarse con una cláusula HAVING:

DELETE dupes
FROM MyTable dupes, MyTable fullTable
WHERE dupes.dupField = fullTable.dupField 
AND dupes.secondDupField = fullTable.secondDupField 
AND dupes.uniqueField > fullTable.uniqueField

La siguiente consulta es útil para eliminar filas duplicadas.La tabla en este ejemplo tiene ID como una columna de identidad y las columnas que tienen datos duplicados son Column1, Column2 y Column3.

DELETE FROM TableName
WHERE  ID NOT IN (SELECT MAX(ID)
                  FROM   TableName
                  GROUP  BY Column1,
                            Column2,
                            Column3
                  /*Even if ID is not null-able SQL Server treats MAX(ID) as potentially
                    nullable. Because of semantics of NOT IN (NULL) including the clause
                    below can simplify the plan*/
                  HAVING MAX(ID) IS NOT NULL) 

El siguiente script muestra el uso de GROUP BY, HAVING, ORDER BY en una consulta y devuelve los resultados con la columna duplicada y su recuento.

SELECT YourColumnName,
       COUNT(*) TotalCount
FROM   YourTableName
GROUP  BY YourColumnName
HAVING COUNT(*) > 1
ORDER  BY COUNT(*) DESC 
delete t1
from table t1, table t2
where t1.columnA = t2.columnA
and t1.rowid>t2.rowid

Postgres:

delete
from table t1
using table t2
where t1.columnA = t2.columnA
and t1.rowid > t2.rowid
DELETE LU 
FROM   (SELECT *, 
               Row_number() 
                 OVER ( 
                   partition BY col1, col1, col3 
                   ORDER BY rowid DESC) [Row] 
        FROM   mytable) LU 
WHERE  [row] > 1 

Esto eliminará las filas duplicadas, excepto la primera fila.

DELETE
FROM
    Mytable
WHERE
    RowID NOT IN (
        SELECT
            MIN(RowID)
        FROM
            Mytable
        GROUP BY
            Col1,
            Col2,
            Col3
    )

Referirse (http://www.codeproject.com/Articles/157977/Remove-Duplicate-Rows-from-a-Table-in-SQL-Server)

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

Recomiendo encarecidamente 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 mantener 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)

Rápido y sucio para eliminar filas duplicadas exactas (para tablas pequeñas):

select  distinct * into t2 from t1;
delete from t1;
insert into t1 select *  from t2;
drop table t2;

Prefiero la solución subconsulta ener recuento(*) > 1 a la combinación interna porque me resultó más fácil de leer y fue muy fácil de convertir en una instrucción SELECT para verificar qué se eliminaría antes de ejecutarlo.

--DELETE FROM table1 
--WHERE id IN ( 
     SELECT MIN(id) FROM table1 
     GROUP BY col1, col2, col3 
     -- could add a WHERE clause here to further filter
     HAVING count(*) > 1
--)

Para recuperar filas duplicadas:

SELECT
name, email, COUNT(*)
FROM 
users
GROUP BY
name, email
HAVING COUNT(*) > 1

Para eliminar las filas duplicadas:

DELETE users 
WHERE rowid NOT IN 
(SELECT MIN(rowid)
FROM users
GROUP BY name, email);      
SELECT  DISTINCT *
      INTO tempdb.dbo.tmpTable
FROM myTable

TRUNCATE TABLE myTable
INSERT INTO myTable SELECT * FROM tempdb.dbo.tmpTable
DROP TABLE tempdb.dbo.tmpTable

Pensé en compartir mi solución ya que funciona en circunstancias especiales.En mi caso, la tabla con valores duplicados no tenía una clave externa (porque los valores estaban duplicados de otra base de datos).

begin transaction
-- create temp table with identical structure as source table
Select * Into #temp From tableName Where 1 = 2

-- insert distinct values into temp
insert into #temp 
select distinct * 
from  tableName

-- delete from source
delete from tableName 

-- insert into source from temp
insert into tableName 
select * 
from #temp

rollback transaction
-- if this works, change rollback to commit and execute again to keep you changes!!

PD:Cuando trabajo en cosas como esta siempre uso una transacción, esto no solo garantiza que todo se ejecute en su totalidad, sino que también me permite realizar pruebas sin arriesgar nada.Pero, por supuesto, deberías hacer una copia de seguridad de todos modos sólo para estar seguro...

Usando CTE.La idea es unir una o más columnas que formen un registro duplicado y luego eliminar las que quieras:

;with cte as (
    select 
        min(PrimaryKey) as PrimaryKey
        UniqueColumn1,
        UniqueColumn2
    from dbo.DuplicatesTable 
    group by
        UniqueColumn1, UniqueColumn1
    having count(*) > 1
)
delete d
from dbo.DuplicatesTable d 
inner join cte on 
    d.PrimaryKey > cte.PrimaryKey and
    d.UniqueColumn1 = cte.UniqueColumn1 and 
    d.UniqueColumn2 = cte.UniqueColumn2;

Se puede encontrar otra solución sencilla en el enlace pegado. aquí.Este es fácil de entender y parece ser efectivo para la mayoría de problemas similares.Es para SQL Server, pero el concepto utilizado es más que aceptable.

Aquí están las partes relevantes de la página vinculada:

Considere estos datos:

EMPLOYEE_ID ATTENDANCE_DATE
A001    2011-01-01
A001    2011-01-01
A002    2011-01-01
A002    2011-01-01
A002    2011-01-01
A003    2011-01-01

Entonces, ¿cómo podemos eliminar esos datos duplicados?

Primero, inserte una columna de identidad en esa tabla usando el siguiente código:

ALTER TABLE dbo.ATTENDANCE ADD AUTOID INT IDENTITY(1,1)  

Utilice el siguiente código para resolverlo:

DELETE FROM dbo.ATTENDANCE WHERE AUTOID NOT IN (SELECT MIN(AUTOID) _
    FROM dbo.ATTENDANCE GROUP BY EMPLOYEE_ID,ATTENDANCE_DATE) 

Esta consulta mostró muy buen rendimiento para mí:

DELETE tbl
FROM
    MyTable tbl
WHERE
    EXISTS (
        SELECT
            *
        FROM
            MyTable tbl2
        WHERE
            tbl2.SameValue = tbl.SameValue
        AND tbl.IdUniqueValue < tbl2.IdUniqueValue
    )

eliminó 1 millón de filas en poco más de 30 segundos de una tabla de 2 millones (50% duplicados)

Aquí hay otro buen artículo sobre eliminando duplicados.

Analiza por qué es difícil:"SQL se basa en álgebra relacional y no pueden ocurrir duplicados en álgebra relacional porque no se permiten duplicados en un conjunto."

La solución de la tabla temporal y dos ejemplos de MySQL.

En el futuro, ¿lo evitará a nivel de base de datos o desde la perspectiva de la aplicación?Yo sugeriría el nivel de la base de datos porque su base de datos debería ser responsable de mantener la integridad referencial, los desarrolladores simplemente causarán problemas;)

Oh, por supuesto.Utilice una tabla temporal.Si desea una declaración única, no muy eficaz, que "funcione", puede optar por:

DELETE FROM MyTable WHERE NOT RowID IN
    (SELECT 
        (SELECT TOP 1 RowID FROM MyTable mt2 
        WHERE mt2.Col1 = mt.Col1 
        AND mt2.Col2 = mt.Col2 
        AND mt2.Col3 = mt.Col3) 
    FROM MyTable mt)

Básicamente, para cada fila de la tabla, la subselección encuentra el ID de fila superior de todas las filas que son exactamente iguales a la fila en consideración.Entonces terminará con una lista de RowID que representan las filas "originales" no duplicadas.

Tenía una tabla donde necesitaba conservar filas no duplicadas.No estoy seguro de la velocidad o la eficiencia.

DELETE FROM myTable WHERE RowID IN (
  SELECT MIN(RowID) AS IDNo FROM myTable
  GROUP BY Col1, Col2, Col3
  HAVING COUNT(*) = 2 )

La otra manera es Crear un nuevo tabla con los mismos campos y con índice único.Entonces mover todos los datos de la tabla antigua a la nueva.Automáticamente SQL SERVER ignora (también hay una opción sobre qué hacer si habrá un valor duplicado:ignorar, interrumpir o algo) valores duplicados.Entonces tenemos la misma tabla sin filas duplicadas. Si no desea un índice único, después de transferir los datos puede eliminarlo..

Especialmente para mesas más grandes puede utilizar DTS (paquete SSIS para importar/exportar datos) para transferir todos los datos rápidamente a su nueva tabla indexada de forma única.Para 7 millones de filas solo lleva unos minutos.

Utilizar esta

WITH tblTemp as
(
SELECT ROW_NUMBER() Over(PARTITION BY Name,Department ORDER BY Name)
   As RowNumber,* FROM <table_name>
)
DELETE FROM tblTemp where RowNumber >1

Al utilizar la siguiente consulta, podemos eliminar registros duplicados en función de una sola columna o de varias columnas.La siguiente consulta se elimina según dos columnas.el nombre de la tabla es: testing y nombres de columnas empno,empname

DELETE FROM testing WHERE empno not IN (SELECT empno FROM (SELECT empno, ROW_NUMBER() OVER (PARTITION BY empno ORDER BY empno) 
AS [ItemNumber] FROM testing) a WHERE ItemNumber > 1)
or empname not in
(select empname from (select empname,row_number() over(PARTITION BY empno ORDER BY empno) 
AS [ItemNumber] FROM testing) a WHERE ItemNumber > 1)
  1. Crea una nueva tabla en blanco con la misma estructura.

  2. Ejecutar consulta como esta

    INSERT INTO tc_category1
    SELECT *
    FROM tc_category
    GROUP BY category_id, application_id
    HAVING count(*) > 1
    
  3. Luego ejecuta esta consulta

    INSERT INTO tc_category1
    SELECT *
    FROM tc_category
    GROUP BY category_id, application_id
    HAVING count(*) = 1
    

Esta es la forma más sencilla de eliminar registros duplicados

 DELETE FROM tblemp WHERE id IN 
 (
  SELECT MIN(id) FROM tblemp
   GROUP BY  title HAVING COUNT(id)>1
 )

http://askme.indianyouth.info/details/how-to-dumplicate-record-from-table-in-using-sql-105

Mencionaría este enfoque porque puede ser útil y funciona en todos los servidores SQL:Muy a menudo solo hay uno: dos duplicados y se conocen los identificadores y el número de duplicados.En este caso:

SET ROWCOUNT 1 -- or set to number of rows to be deleted
delete from myTable where RowId = DuplicatedID
SET ROWCOUNT 0

Desde el nivel de la aplicación (desafortunadamente).Estoy de acuerdo en que la forma correcta de evitar la duplicación es a nivel de base de datos mediante el uso de un índice único, pero en SQL Server 2005, se permite que un índice tenga solo 900 bytes, y mi campo varchar(2048) lo elimina.

No sé qué tan bien funcionaría, pero creo que se podría escribir un disparador para hacer cumplir esto, incluso si no se pudiera hacerlo directamente con un índice.Algo como:

-- given a table stories(story_id int not null primary key, story varchar(max) not null)
CREATE TRIGGER prevent_plagiarism 
ON stories 
after INSERT, UPDATE 
AS 
    DECLARE @cnt AS INT 

    SELECT @cnt = Count(*) 
    FROM   stories 
           INNER JOIN inserted 
                   ON ( stories.story = inserted.story 
                        AND stories.story_id != inserted.story_id ) 

    IF @cnt > 0 
      BEGIN 
          RAISERROR('plagiarism detected',16,1) 

          ROLLBACK TRANSACTION 
      END 

Además, varchar(2048) me suena sospechoso (algunas cosas en la vida tienen 2048 bytes, pero es bastante poco común);¿Realmente no debería ser varchar(max)?

DELETE
FROM
    table_name T1
WHERE
    rowid > (
        SELECT
            min(rowid)
        FROM
            table_name T2
        WHERE
            T1.column_name = T2.column_name
    );
CREATE TABLE car(Id int identity(1,1), PersonId int, CarId int)

INSERT INTO car(PersonId,CarId)
VALUES(1,2),(1,3),(1,2),(2,4)

--SELECT * FROM car

;WITH CTE as(
SELECT ROW_NUMBER() over (PARTITION BY personid,carid order by personid,carid) as rn,Id,PersonID,CarId from car)

DELETE FROM car where Id in(SELECT Id FROM CTE WHERE rn>1)

Si desea obtener una vista previa de las filas que está a punto de eliminar y mantener el control sobre cuáles de las filas duplicadas conservar.Ver http://developer.azurewebsites.net/2014/09/better-sql-group-by-find-duplicate-data/

with MYCTE as (
  SELECT ROW_NUMBER() OVER (
    PARTITION BY DuplicateKey1
                ,DuplicateKey2 -- optional
    ORDER BY CreatedAt -- the first row among duplicates will be kept, other rows will be removed
  ) RN
  FROM MyTable
)
DELETE FROM MYCTE
WHERE RN > 1
DELETE 
FROM MyTable
WHERE NOT EXISTS (
              SELECT min(RowID)
              FROM Mytable
              WHERE (SELECT RowID 
                     FROM Mytable
                     GROUP BY Col1, Col2, Col3
                     ))
               );
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top