Come eliminare le righe duplicate in SQL Server?


416

Come posso eliminare le righe duplicate dove non unique row idesiste?

Il mio tavolo è

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

Voglio essere lasciato con il seguente dopo la rimozione duplicata:

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

Ho provato alcune query, ma penso che dipendono dall'avere un ID riga poiché non ottengo il risultato desiderato. Per esempio:

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

5
Questo non è un duplicato del primo collegamento. In questa domanda non esiste un ID riga e nella domanda collegata è presente un ID riga. Molto diverso.
Alien Technology

cambia 'SELEZIONA ID DA tabella Raggruppando ID CON HAVING' per avere una funzione aggregata, ad es. MAX / MIN e dovrebbe funzionare.
incasinato il

Risposte:


786

Mi piacciono i CTE e ROW_NUMBERpoiché i due combinati ci consentono di vedere quali righe vengono eliminate (o aggiornate), quindi basta cambiare DELETE FROM CTE...in 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 (il risultato è diverso; suppongo sia dovuto a un refuso da parte tua)

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

Questo esempio determina i duplicati di una singola colonna a col1causa di PARTITION BY col1. Se vuoi includere più colonne, aggiungile semplicemente a PARTITION BY:

ROW_NUMBER()OVER(PARTITION BY Col1, Col2, ... ORDER BY OrderColumn)

2
Grazie per un'ottima risposta MSFT, al contrario, ha una risposta molto complicata qui: stackoverflow.com/questions/18390574/…
Barka,

2
@ omachu23: in questo caso non importa, anche se penso che sia più efficiente nel CTE che al di fuori ( AND COl1='John'). Normalmente è necessario applicare il filtro nel CTE.
Tim Schmelter,

1
@ omachu23: è possibile utilizzare qualsiasi SQL nella CTE (a parte l'ordinazione), quindi se si desidera filtrare dalla Johns: ...FROM dbo.Table1 WHERE Col1='John'. Ecco il violino: sqlfiddle.com/#!6/fae73/744/0
Tim Schmelter,

1
La soluzione più semplice potrebbe essere proprio set rowcount 1 delete from t1 where col1=1 and col2=1quella vista qui
Zorgarath,

15
Questa risposta eliminerà solo le righe che hanno duplicati in col1. Aggiungi le colonne nella "selezione" alla "partizione per", ad esempio utilizzando la selezione nella risposta: RN = ROW_NUMBER () OVER (PARTITION BY col1, col2, col3, col4, col5, col6, col7 ORDER BY col1)
rlee

159

Preferirei CTE per eliminare le righe duplicate dalla tabella del server sql

consiglio vivamente di seguire questo articolo: http://codaffection.com/sql-server-article/delete-duplicate-rows-in-sql-server/

mantenendo l'originale

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

senza mantenere l'originale

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)

2
La funzione di windowing è un'ottima soluzione.
Robert Casey,

2
Sono un po 'confuso. L'hai eliminato da CTE non dalla tabella originale. Quindi, come funziona?
Bigeyes,

8
La cancellazione di record da CTE da parte di @Bigeyes rimuoverà i record corrispondenti dalla tabella fisica effettiva (poiché CTE contiene riferimenti a record effettivi).
Shamseer K,

Non avevo idea che fosse così fino a questo post ... Grazie
Zakk Diaz,

1
Perché vorresti eliminare sia l'originale che il suo duplicato? Non capisco perché non vorresti semplicemente rimuovere il duplicato e conservare l'altro.
Ricco

52

Senza usare CTEe ROW_NUMBER()puoi semplicemente cancellare i record semplicemente usando group by con la MAXfunzione qui è ed esempio

DELETE
FROM MyDuplicateTable
WHERE ID NOT IN
(
SELECT MAX(ID)
FROM MyDuplicateTable
GROUP BY DuplicateColumn1, DuplicateColumn2, DuplicateColumn3)

4
Questa query eliminerà i record non duplicati.
Derek Smalls,

8
Funziona bene, grazie. @Derek Ricorda che questo non rimuove i miei record non duplicati.
monteirobrena,

1
Oppure puoi conservare i documenti originali 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
)

Non potresti riscrivere in: where id in (seleziona max (id) ... avendo count (*)> 1)?
Brent,

1
Non credo ci sia bisogno di avere have o union, questo sarà sufficiente: elimina dalla ricerca dove ID non è presente (seleziona min (id) dal gruppo di ricerca per url)
Christopher Yang

9

Consultare anche la seguente modalità di eliminazione.

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)

Creata una tabella di esempio denominata @tablee caricata con dati specifici.

inserisci qui la descrizione dell'immagine

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

inserisci qui la descrizione dell'immagine

Nota: se si stanno dando tutte le colonne nella Partition byparte, allora order bynon hanno molto significato.

Lo so, la domanda è stata posta tre anni fa, e la mia risposta è un'altra versione di ciò che Tim ha pubblicato, ma pubblicare solo in caso sia utile per chiunque.


9

Se non hai riferimenti, come le chiavi esterne, puoi farlo. Lo faccio molto quando collaudo le prove concettuali e i dati dei test vengono duplicati.

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

INTO [newTable]

Vai in Esplora oggetti ed elimina la vecchia tabella.

Rinominare la nuova tabella con il nome della tabella precedente.


Questo è il modo più semplice che ho imparato nei miei materiali introduttivi e che utilizzo.
Eric

7

Microsoft ha una guida molto chiara su come rimuovere i duplicati. Dai un'occhiata a http://support.microsoft.com/kb/139444

In breve, ecco il modo più semplice per eliminare i duplicati quando hai solo poche righe da eliminare:

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

myprimarykey è l'identificatore per la riga.

Ho impostato il conteggio delle righe su 1 perché avevo solo due righe duplicate. Se avessi duplicato 3 righe, avrei impostato Rowcount su 2 in modo da eliminare i primi due che vede e lasciarne solo uno nella tabella t1.

Spero che aiuti chiunque


1
Come faccio a sapere quante righe ho duplicato se ho 10k righe?
Fearghal,

@Fearghal prova "seleziona primaryKey, conta (*) dal gruppo myTable di primaryKey;"
oabarca,

1
E se ci fossero numeri variabili di righe duplicate? cioè la riga a ha 2 record e la riga b ha 5 record e la riga c non ha record duplicati
thermite

1
@ user2070775 Che cosa succede se solo un sottoinsieme di tutte le righe ha duplicati e di questi duplicati alcuni vengono duplicati due volte e tre o quattro volte?
thermite,

@ user2070775 Ho perso la parte in cui hai detto "solo poche righe da eliminare". Inoltre c'è un avvertimento sulla pagina sul set rowcount che nelle future versioni di sql non influenzerà le istruzioni di aggiornamento o cancellazione
thermite,

6

Prova ad usare:

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

inserisci qui la descrizione dell'immagine


4

Dopo aver provato la soluzione suggerita sopra, che funziona per piccoli tavoli medi. Posso suggerire quella soluzione per tavoli molto grandi. poiché funziona in iterazioni.

  1. Elimina tutte le visualizzazioni di dipendenza di LargeSourceTable
  2. puoi trovare le dipendenze usando sql managment studio, fai clic destro sulla tabella e fai clic su "Visualizza dipendenze"
  3. Rinomina la tabella:
  4. sp_rename 'LargeSourceTable', 'LargeSourceTable_Temp'; GO
  5. Crea di LargeSourceTablenuovo, ma ora aggiungi una chiave primaria con tutte le colonne che definiscono le duplicazioniWITH (IGNORE_DUP_KEY = ON)
  6. Per esempio:

    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. Crea di nuovo le viste che hai lasciato cadere in primo luogo per la nuova tabella creata

  8. Ora, esegui il seguente script sql, vedrai i risultati in 1.000.000 di righe per pagina, puoi cambiare il numero di riga per pagina per vedere i risultati più spesso.

  9. Si noti che ho attivato IDENTITY_INSERTe disattivato perché una delle colonne contiene un ID incrementale automatico, che sto anche 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


4

Esistono due soluzioni in mysql:

A) Elimina le righe duplicate usando l' DELETE JOINistruzione

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

Questa query fa riferimento alla tabella dei contatti due volte, pertanto utilizza l'alias di tabella t1e t2.

L'output è:

1 query OK, 4 righe interessate (0,10 sec)

Nel caso in cui si desideri eliminare righe duplicate e conservare il lowest id, è possibile utilizzare la seguente istruzione:

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

   

B) Elimina le righe duplicate usando una tabella intermedia

Di seguito sono illustrati i passaggi per la rimozione di righe duplicate utilizzando una tabella intermedia:

    1. Creare una nuova tabella con la stessa struttura della tabella originale in cui si desidera eliminare le righe duplicate.

    2. Inserire righe distinte dalla tabella originale alla tabella immediata.

    3. Inserire righe distinte dalla tabella originale alla tabella immediata.

 

Passaggio 1. Creare una nuova tabella la cui struttura è la stessa della tabella originale:

CREATE TABLE source_copy LIKE source;

Passaggio 2. Inserire righe distinte dalla tabella originale alla nuova tabella:

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

Passaggio 3. rilasciare la tabella originale e rinominare la tabella immediata con quella originale

DROP TABLE source;
ALTER TABLE source_copy RENAME TO source;

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


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

2

È necessario raggruppare i record duplicati in base ai campi, quindi tenere uno dei record ed eliminare il resto. Per esempio:

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)

2

L'eliminazione di duplicati da una tabella enorme (diversi milioni di record) potrebbe richiedere molto tempo. Ti suggerisco di eseguire un inserimento in blocco in una tabella temporanea delle righe selezionate anziché eliminare.

--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;

2

Può essere fatto in molti modi nel server SQL il modo più semplice per farlo è: inserire le righe distinte dalla tabella delle righe duplicate alla nuova tabella temporanea. Quindi eliminare tutti i dati dalla tabella delle righe duplicate, quindi inserire tutti i dati dalla tabella temporanea che non ha duplicati come mostrato di seguito.

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

   select * from table

Elimina le righe duplicate utilizzando 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

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

1

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

L'idea di rimuovere i duplicati comporta

  • a) Proteggere quelle righe che non sono duplicate
  • b) Conservare una delle molte righe che si sono qualificate insieme come duplicate.

Passo dopo passo

  • 1) Innanzitutto identifica le righe che soddisfano la definizione di duplicato e inseriscile nella tabella temporanea, ad esempio #tableAll.
  • 2) Selezionare le righe non duplicate (a riga singola) o distinte nella tabella temporanea, ad esempio #tableUnique.
  • 3) Elimina dalla tabella di origine unendo #tableAll per eliminare i duplicati.
  • 4) Inserire nella tabella di origine tutte le righe da #tableUnique.
  • 5) Rilasciare #tableAll e #tableUnique

1

Se hai la possibilità di aggiungere temporaneamente una colonna alla tabella, questa è stata una soluzione che ha funzionato per me:

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

Quindi eseguire una ELIMINA usando una combinazione di 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
                 );

Verificare che DELETE sia stato eseguito correttamente:

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 

Il risultato non dovrebbe avere righe con un conteggio maggiore di 1. Infine, rimuovere la colonna rowid:

ALTER TABLE dbo.DUPPEDTABLE DROP COLUMN RowID;

0

Un altro modo per rimuovere le righe pubblicate senza perdere informazioni in un solo passaggio è come il seguente:

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

0

Oh wow, mi sento così stupido pronto per tutte queste risposte, sono come la risposta degli esperti con tutte le tabelle CTE e temporanee ecc.

E tutto quello che ho fatto per farlo funzionare era semplicemente aggregare la colonna ID usando MAX.

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

NOTA: potrebbe essere necessario eseguirlo più volte per rimuovere i duplicati in quanto ciò eliminerà solo un set di righe duplicate alla volta.


Questo non funzionerà poiché rimuoverà tutti i duplicati senza lasciare gli originali. OP chiede di conservare i record originali.
0xdd,

2
Non vero, max ti fornirà un ID massimo che soddisfa le condizioni. Se ciò non è vero, dimostra la tua richiesta di voto negativo.
incasinato il

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;

L'eliminazione dei duplicati da una tabella enorme (diversi milioni di record) potrebbe richiedere molto tempo. Ti suggerisco di fare un inserimento in blocco in una tabella temporanea delle righe selezionate piuttosto che eliminare. '- RECRITANDO IL TUO CODICE (PRENDI NOTA DELLA 3ª LINEA) CON CTE AS (SELEZIONA NOME, ROW_NUMBER () OVER (PARTITION BY NAME ORDER BY NAME) ID DA @TB) SELEZIONA * IN #unique_records DA CTE DOVE 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))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.