Domanda

Qual è il modo migliore per rimuovere le righe duplicate da un file abbastanza grande SQL Server tabella (es.300.000+ righe)?

Le righe, ovviamente, non saranno duplicati perfetti a causa dell'esistenza del file RowID campo identitario.

MyTable

RowID int not null identity(1,1) primary key,
Col1 varchar(20) not null,
Col2 varchar(2048) not null,
Col3 tinyint not null
È stato utile?

Soluzione

Supponendo che non ci siano valori nulli, tu GROUP BY le colonne univoche e SELECT IL MIN (or MAX) RowId come riga da mantenere.Quindi, elimina semplicemente tutto ciò che non aveva un ID riga:

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

Nel caso in cui tu abbia un GUID invece di un numero intero, puoi sostituirlo

MIN(RowId)

con

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

Altri suggerimenti

Un altro modo possibile per farlo è

; 

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

sto usando ORDER BY (SELECT 0) sopra in quanto è arbitrario quale riga conservare in caso di parità.

Per preservare l'ultimo arrivato RowID ordina ad esempio che potresti usare ORDER BY RowID DESC

Piani di esecuzione

Il piano di esecuzione per questo è spesso più semplice ed efficiente di quello nella risposta accettata poiché non richiede l'auto-unione.

Execution Plans

Tuttavia non è sempre così.Un posto dove il GROUP BY la soluzione potrebbe essere preferita nelle situazioni in cui a aggregato di hash verrebbe scelto preferibilmente rispetto a un aggregato di flussi.

IL ROW_NUMBER la soluzione fornirà sempre più o meno lo stesso piano mentre il GROUP BY la strategia è più flessibile.

Execution Plans

I fattori che potrebbero favorire l’approccio aggregato dell’hash sarebbero

  • Nessun indice utile nelle colonne di partizionamento
  • relativamente meno gruppi con relativamente più duplicati in ciascun gruppo

Nelle versioni estreme di questo secondo caso (se ci sono pochissimi gruppi con molti duplicati in ciascuno) si potrebbe anche considerare semplicemente di inserire le righe da conservare in una nuova tabella quindi TRUNCATE-recuperare gli originali e copiarli nuovamente per ridurre al minimo la registrazione rispetto all'eliminazione di una percentuale molto elevata di righe.

C'è un bell'articolo su rimozione dei duplicati sul sito del supporto Microsoft.È piuttosto conservativo: ti fanno fare tutto in passaggi separati, ma dovrebbe funzionare bene su tavoli di grandi dimensioni.

Ho utilizzato i self-join per farlo in passato, anche se probabilmente potrebbe essere abbellito con una clausola HAVING:

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

La seguente query è utile per eliminare righe duplicate.La tabella in questo esempio ha ID come colonna identità e le colonne che contengono dati duplicati lo sono Column1, Column2 E 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) 

Lo script seguente mostra l'utilizzo di GROUP BY, HAVING, ORDER BY in una query e restituisce i risultati con la colonna duplicata e il relativo conteggio.

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

Postgre:

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 

Ciò eliminerà le righe duplicate, tranne la prima riga

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

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

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)

Quick and Dirty per eliminare le righe duplicate esatte (per tabelle di piccole dimensioni):

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

Preferisco la soluzione subquery\have count(*) > 1 all'inner join perché l'ho trovata più facile da leggere ed è stato molto facile trasformarla in un'istruzione SELECT per verificare cosa sarebbe stato eliminato prima di eseguirlo.

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

Per recuperare righe duplicate:

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

Per eliminare le righe duplicate:

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

Ho pensato di condividere la mia soluzione poiché funziona in circostanze speciali.Nel mio caso la tabella con valori duplicati non aveva una chiave esterna (perché i valori erano duplicati da un altro db).

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

PS:quando lavoro su cose del genere utilizzo sempre una transazione, questo non solo garantisce che tutto venga eseguito nel suo insieme, ma mi permette anche di testare senza rischiare nulla.Ma ovviamente dovresti comunque fare un backup per essere sicuro...

Utilizzando CTE.L'idea è di unire una o più colonne che formano un record duplicato e quindi rimuovere quella che preferisci:

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

Ancora un'altra soluzione semplice può essere trovata nel collegamento incollato Qui.Questo è facile da comprendere e sembra essere efficace per la maggior parte dei problemi simili.È per SQL Server, ma il concetto utilizzato è più che accettabile.

Ecco le parti rilevanti dalla pagina collegata:

Considera questi dati:

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

Allora come possiamo eliminare quei dati duplicati?

Innanzitutto, inserisci una colonna Identity in quella tabella utilizzando il seguente codice:

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

Utilizzare il seguente codice per risolverlo:

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

Questa query ha mostrato ottime prestazioni per me:

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

ha eliminato 1 milione di righe in poco più di 30 secondi da una tabella di 2 milioni (50% di duplicati)

Ecco un altro buon articolo su rimozione dei duplicati.

Spiega perché è difficile:"SQL è basato sull'algebra relazionale e i duplicati non possono verificarsi nell'algebra relazionale, perché i duplicati non sono consentiti in un set."

La soluzione della tabella temporanea e due esempi MySQL.

In futuro lo impedirai a livello di database o dal punto di vista dell'applicazione.Suggerirei il livello del database perché il tuo database dovrebbe essere responsabile del mantenimento dell'integrità referenziale, gli sviluppatori causerebbero solo problemi;)

Oh certo.Utilizza una tabella temporanea.Se vuoi una singola affermazione non molto performante che "funzioni" puoi scegliere:

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)

Fondamentalmente, per ogni riga della tabella, la sottoselezione trova il RowID superiore di tutte le righe che sono esattamente come la riga in questione.Quindi ti ritroverai con un elenco di RowID che rappresentano le righe "originali" non duplicate.

Avevo una tabella in cui dovevo preservare le righe non duplicate.Non sono sicuro della velocità o dell'efficienza.

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

L'altro modo è Creane uno nuovo tabella con gli stessi campi e con indice univoco.Poi spostare tutti i dati dalla vecchia tabella alla nuova tabella.SQL SERVER ignora automaticamente (c'è anche un'opzione su cosa fare se sarà presente un valore duplicato:ignorare, interrompere o qc) valori duplicati.Quindi abbiamo la stessa tabella senza righe duplicate. Se non desideri l'Indice Univoco, dopo il trasferimento dei dati puoi eliminarlo.

Particolarmente per tavoli più grandi puoi utilizzare DTS (pacchetto SSIS per importare/esportare dati) per trasferire rapidamente tutti i dati nella tua nuova tabella indicizzata in modo univoco.Per 7 milioni di righe bastano pochi minuti.

Usa questo

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

Utilizzando la query seguente possiamo eliminare record duplicati in base alla colonna singola o a più colonne.la query seguente viene eliminata in base a due colonne.il nome della tabella è: testing e nomi di colonne 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 nuova tabella vuota con la stessa struttura

  2. Esegui query in questo modo

    INSERT INTO tc_category1
    SELECT *
    FROM tc_category
    GROUP BY category_id, application_id
    HAVING count(*) > 1
    
  3. Quindi esegui questa query

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

Questo è il modo più semplice per eliminare i record duplicati

 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

Vorrei menzionare questo approccio perché può essere utile e funziona su tutti i server SQL:Molto spesso ci sono solo uno o due duplicati e gli ID e il conteggio dei duplicati sono noti.In questo caso:

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

Dal livello applicativo (purtroppo).Sono d'accordo che il modo corretto per prevenire la duplicazione è a livello di database attraverso l'uso di un indice univoco, ma in SQL Server 2005 un indice può contenere solo 900 byte e il mio campo varchar(2048) lo spazza via.

Non so quanto funzionerebbe, ma penso che potresti scrivere un trigger per applicarlo, anche se non potresti farlo direttamente con un indice.Qualcosa di simile a:

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

Inoltre, varchar(2048) mi sembra sospetto (alcune cose nella vita sono 2048 byte, ma è piuttosto raro);non dovrebbe davvero essere 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)

Vuoi visualizzare in anteprima le righe che stai per rimuovere e mantenere il controllo su quali righe duplicate conservare.Vedere 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
                     ))
               );
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top