Como excluir linhas duplicadas no SQL Server?

415

Como posso excluir linhas duplicadas onde não unique row idexistem?

Minha mesa é

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

Quero ser deixado com o seguinte após a remoção duplicada:

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

Eu tentei algumas consultas, mas acho que elas dependem de ter um ID de linha, pois não obtenho o resultado desejado. Por exemplo:

DELETE
FROM table
WHERE col1 IN (
    SELECT id
    FROM table
    GROUP BY id
    HAVING (COUNT(col1) > 1)
)
Fearghal
fonte
5
Este não é um engano do primeiro link. Nesta pergunta, não há ID da linha e na pergunta vinculada, há um ID da linha. Muito diferente.
Alien Technology
altere 'SELECT id FROM tabela GROUP BY id HAVING' para ter uma função agregada, por exemplo, MAX / MIN e deve funcionar.
messed-up

Respostas:

785

Eu gosto de CTEs e, ROW_NUMBERcomo os dois combinados nos permitem ver quais linhas são excluídas (ou atualizadas), basta alterar o valor DELETE FROM CTE...para 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 (o resultado é diferente; presumo que seja devido a um erro de digitação de sua parte)

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

Este exemplo determina duplicatas por uma única coluna col1devido ao PARTITION BY col1. Se você deseja incluir várias colunas, basta adicioná-las ao PARTITION BY:

ROW_NUMBER()OVER(PARTITION BY Col1, Col2, ... ORDER BY OrderColumn)
Tim Schmelter
fonte
2
Obrigado por uma ótima resposta. MSFT em contraste tem uma resposta muito complicado aqui: stackoverflow.com/questions/18390574/...
Barka
2
@ omachu23: neste caso, não importa, embora eu ache que seja mais eficiente no CTE do que fora ( AND COl1='John'). Normalmente você deve aplicar o filtro no CTE.
Tim Schmelter
1
@ omachu23: você pode usar qualquer SQL no CTE (além de ordenação), então se você quiser para filtrar por Johns: ...FROM dbo.Table1 WHERE Col1='John'. Aqui está o violino: sqlfiddle.com/#!6/fae73/744/0
Tim Schmelter
1
A solução mais fácil pode apenas ser set rowcount 1 delete from t1 where col1=1 and col2=1visto aqui
Zorgarath
15
Esta resposta excluirá apenas as linhas que tenham duplicatas em col1. Adicione as colunas na partição "selecionar" para "por", por exemplo, usando a opção selecionar na resposta: RN = ROW_NUMBER () OVER (PARTITION BY col1, col2, col3, col4, col4, col5, col6, col7 ORDER BY col1)
rlee
159

Eu preferiria o CTE para excluir linhas duplicadas da tabela do servidor sql

é altamente recomendável seguir este artigo :: http://codaffection.com/sql-server-article/delete-duplicate-rows-in-sql-server/

mantendo 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

sem manter o 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
fonte
2
A função de janelas é uma ótima solução.
Robert Casey
2
Estou um pouco confuso. Você o excluiu do CTE e não da tabela original. Então, como isso funciona?
Bigeyes
8
Os @Bigeyes que excluem registros do CTE removerão os registros correspondentes da tabela física real (porque o CTE contém referência aos registros reais).
Shamseer K
Eu não tinha idéia este foi o caso até este post ... Obrigado
Zakk Diaz
1
Por que você deseja excluir o original e sua duplicata? Não estou entendendo por que você não deseja remover a duplicata e manter a outra.
Rich
52

Sem usar CTEe ROW_NUMBER()você pode simplesmente excluir os registros apenas usando group by with MAXfunction aqui está e exemplo

DELETE
FROM MyDuplicateTable
WHERE ID NOT IN
(
SELECT MAX(ID)
FROM MyDuplicateTable
GROUP BY DuplicateColumn1, DuplicateColumn2, DuplicateColumn3)
Aamir
fonte
4
Esta consulta excluirá registros não duplicados.
Derek Smalls
8
Isso funciona bem, obrigado. @DerekSmalls isso não remove meus registros não duplicados.
monteirobrena
1
Ou você pode manter os registros originais usando #MIN(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
fonte
Não foi possível reescrever para: onde id (selecione max (id) ... tendo contagem (*)> 1)?
Brent
1
Eu não acredito que haja qualquer necessidade de usar ter ou união, isso será suficiente: exclusão de busca onde id não in (select min (id) do grupo de busca por url)
Christopher Yang
9

Por favor, veja também a forma de exclusão abaixo.

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)

Criou uma tabela de amostra chamada @tablee carregou-a com os dados fornecidos.

insira a descrição da imagem aqui

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

insira a descrição da imagem aqui

Nota: Se você estiver fornecendo todas as colunas da Partition bypeça, order bynão terá muito significado.

Eu sei, a pergunta foi feita há três anos e minha resposta é outra versão do que Tim postou, mas postar apenas no caso de ajuda é útil para qualquer pessoa.

Jithin Shaji
fonte
9

Se você não tiver referências, como chaves estrangeiras, poderá fazer isso. Faço isso muito ao testar provas de conceito e os dados de teste são duplicados.

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

INTO [newTable]

Vá para o explorador de objetos e exclua a tabela antiga.

Renomeie a nova tabela com o nome da tabela antiga.

Rhys
fonte
Essa é a maneira mais simples que aprendi em meus materiais de introdução e que uso.
eric
7

A Microsoft tem um guia muito simples sobre como remover duplicatas. Verificação de saída http://support.microsoft.com/kb/139444

Em resumo, eis a maneira mais fácil de excluir duplicatas quando você tiver apenas algumas linhas para excluir:

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

myprimarykey é o identificador para a linha.

Eu configurei o número de linhas como 1 porque só tinha duas linhas duplicadas. Se eu tivesse três linhas duplicadas, teria definido o número de linhas como 2, para excluir as duas primeiras que vê e deixar apenas uma na tabela t1.

Espero que ajude alguém

oabarca
fonte
1
Como sei quantas linhas duplicaram se tenho 10 mil linhas?
Fearghal
@Fearghal try "selecione primaryKey, count (*) do grupo myTable por primaryKey;"
oabarca
1
Mas e se houver um número variável de linhas duplicadas? ou seja, uma linha tem 2 fichas e linha b tem registos 5 e linha C não tem registos duplicados
térmite
1
@ user2070775 E se apenas um subconjunto de todas as linhas tiver duplicatas, e dessas duplicadas algumas forem duplicadas duas vezes e outras três ou quatro vezes?
Termite
@ user2070775 Perdi a parte em que você disse "apenas algumas linhas para excluir". Também há um aviso na página sobre set rowcount que em futuras versões do SQL que costuma afetar atualização ou DELETE
thermite
6

Tente usar:

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

insira a descrição da imagem aqui

Fezal halai
fonte
4

Depois de tentar a solução sugerida acima, isso funciona para pequenas tabelas médias. Eu posso sugerir essa solução para tabelas muito grandes. uma vez que é executado em iterações.

  1. Elimine todas as visualizações de dependência do LargeSourceTable
  2. você pode encontrar as dependências usando o sql managment studio, clique com o botão direito do mouse na tabela e clique em "Exibir dependências"
  3. Renomeie a tabela:
  4. sp_rename 'LargeSourceTable', 'LargeSourceTable_Temp'; GO
  5. Crie LargeSourceTablenovamente, mas agora, adicione uma chave primária com todas as colunas que definem as duplicaçõesWITH (IGNORE_DUP_KEY = ON)
  6. Por exemplo:

    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. Crie novamente as visualizações que você soltou em primeiro lugar para a nova tabela criada

  8. Agora, execute o seguinte script sql, você verá os resultados em 1.000.000 de linhas por página, poderá alterar o número da linha por página para ver os resultados com mais frequência.

  9. Observe que eu ativei IDENTITY_INSERTe desativei porque uma das colunas contém um ID incremental automático, que também estou 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
fonte
4

Existem duas soluções em mysql:

A) Excluir linhas duplicadas usando a DELETE JOINinstrução

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

Esta consulta faz referência à tabela de contatos duas vezes; portanto, ela usa o alias da tabela t1et2 .

A saída é:

1 Consulta OK, 4 linhas afetadas (0,10 s)

Caso deseje excluir linhas duplicadas e manter o lowest id, você pode usar a seguinte instrução:

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

   

B) Excluir linhas duplicadas usando uma tabela intermediária

A seguir, são mostradas as etapas para remover linhas duplicadas usando uma tabela intermediária:

    1. Crie uma nova tabela com a estrutura igual à tabela original que você deseja excluir linhas duplicadas.

    2. Insira linhas distintas da tabela original na tabela imediata.

    3. Insira linhas distintas da tabela original na tabela imediata.

 

Etapa 1. Crie uma nova tabela cuja estrutura seja igual à tabela original:

CREATE TABLE source_copy LIKE source;

Etapa 2. Insira linhas distintas da tabela original na nova tabela:

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

Etapa 3. Solte a tabela original e renomeie a tabela imediata para a original

DROP TABLE source;
ALTER TABLE source_copy RENAME TO source;

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

Bashirpour
fonte
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
fonte
2

Você precisa agrupar os registros duplicados de acordo com o (s) campo (s), depois manter um dos registros e excluir o restante. Por exemplo:

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
fonte
2

A exclusão de duplicatas de uma tabela enorme (vários milhões de registros) pode levar muito tempo. Sugiro que você faça uma inserção em massa em uma tabela temporária das linhas selecionadas, em vez de excluir.

--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
fonte
2

Isso pode ser feito de várias maneiras no servidor sql. A maneira mais simples de fazer isso é: Insira as linhas distintas da tabela de linhas duplicadas na nova tabela temporária. Em seguida, exclua todos os dados da tabela de linhas duplicadas e insira todos os dados da tabela temporária que não possui duplicatas, como mostrado abaixo.

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

   select * from table

Excluir linhas duplicadas usando o Common Table Expression (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
fonte
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
fonte
1

Com referência a https://support.microsoft.com/en-us/help/139444/how-to-remove-duplicate-rows-from-a-table-in-sql-server

A idéia de remover duplicados envolve

  • a) Protegendo as linhas que não são duplicadas
  • b) Mantenha uma das muitas linhas que se qualificaram juntas como duplicadas.

Passo a passo

  • 1) Primeiro identifique as linhas que atendem à definição de duplicado e insira-as na tabela temporária, diga #tableAll.
  • 2) Selecione linhas não duplicadas (linhas únicas) ou distintas na tabela temporária, diga #tableUnique.
  • 3) Exclua da tabela de origem juntando-se a #tableAll para excluir as duplicatas.
  • 4) Insira na tabela de origem todas as linhas de #tableUnique.
  • 5) Solte #tableAll e #tableUnique
rajibdotnet
fonte
1

Se você tiver a capacidade de adicionar uma coluna à tabela temporariamente, esta foi uma solução que funcionou para mim:

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

Em seguida, execute um DELETE usando uma combinação de MIN e 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 se o DELETE foi executado corretamente:

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 

O resultado não deve ter linhas com uma contagem maior que 1. Por fim, remova a coluna rowid:

ALTER TABLE dbo.DUPPEDTABLE DROP COLUMN RowID;
j.hull
fonte
0

Outra maneira de remover linhas publicadas, sem perder informações em uma etapa, é como a seguir:

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
fonte
0

Oh uau, eu me sinto tão estúpido por preparar todas essas respostas, elas são como a resposta de especialistas com todas as tabelas CTE e temporárias e etc.

E tudo o que fiz para fazê-lo funcionar foi simplesmente agregar a coluna ID usando o MAX.

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

NOTA: pode ser necessário executá-lo várias vezes para remover duplicados, pois isso excluirá apenas um conjunto de linhas duplicadas por vez.

bagunçado
fonte
Isso não funcionará, pois removerá todas as duplicatas sem deixar os originais. O OP está pedindo para preservar os registros originais.
0xdd 17/07/2018
2
Não é verdade, max fornecerá o ID máximo que satisfaz a condição. Se isso não for verdade, prove seu argumento para voto negativo.
0/0:
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
fonte
A exclusão de duplicatas de uma tabela enorme (vários milhões de registros) pode levar muito tempo. Sugiro que você faça uma inserção em massa em uma tabela temporária das linhas selecionadas em vez de excluir. '--RECREVENDO SEU CÓDIGO (TOMAR NOTA DA TERCEIRA LINHA) COM CTE AS (SELECT NAME, ROW_NUMBER () OVER (PARTIÇÃO POR NOME ORDEM POR NOME) ID FROM @TB) SELECT * INTO #unique_records FROM CTE WHERE ID = 1; "
Emmanuel Touro
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
fonte