2012-06-21 3 views
5

Ho commesso un errore in uno script di inserimento di massa, quindi ora ho righe "duplicate" con diversi colX. Devo cancellare queste righe duplicate, ma non riesco a capire come. Per essere più precisi, ho questo:Elimina righe "duplicate" in SQL Server 2010

col1 | col2 | col3 | colX  
----+---------------------- 
    0 | 1 | 2 | a 
    0 | 1 | 2 | b 
    0 | 1 | 2 | c 
    0 | 1 | 2 | a 
    3 | 4 | 5 | x 
    3 | 4 | 5 | y 
    3 | 4 | 5 | x 
    3 | 4 | 5 | z 

e voglio mantenere la prima occorrenza di ciascuna (riga, colX):

col1 | col2 | col3 | colX  
----+---------------------- 
    0 | 1 | 2 | a 
    3 | 4 | 5 | x 

Grazie per le vostre risposte :)

+2

tabelle di database non hanno il concetto di ordine fila. Vuoi ordinare per min (colX) e mantenere quelle file? C'è una colonna timestamp sulla riga? –

+3

Quale versione di SQL Server stai usando? Per quanto ne so, non esiste SQL Server 2010. –

+0

Se si ha '0 | 1 | 3 | a 'sui tuoi dati, dovrebbe essere tenuto? o dovrebbe essere rimosso? –

risposta

10

Prova l'approccio più semplice con il CTE di SQL Server: http://www.sqlfiddle.com/#!3/2d386/2

dati:

CREATE TABLE tbl 
    ([col1] int, [col2] int, [col3] int, [colX] varchar(1)); 

INSERT INTO tbl 
    ([col1], [col2], [col3], [colX]) 
VALUES 
    (0, 1, 2, 'a'), 
    (0, 1, 2, 'b'), 
    (0, 1, 2, 'c'), 
    (0, 1, 2, 'a'), 
    (3, 4, 5, 'x'), 
    (3, 4, 5, 'y'), 
    (3, 4, 5, 'x'), 
    (3, 4, 5, 'z'); 

Soluzione:

select * from tbl; 

with a as 
(
    select row_number() over(partition by col1 order by col2, col3, colX) as rn 
    from tbl 
) 
delete from a where rn > 1; 

select * from tbl; 

outpu t:

| COL1 | COL2 | COL3 | COLX | 
----------------------------- 
| 0 | 1 | 2 | a | 
| 0 | 1 | 2 | b | 
| 0 | 1 | 2 | c | 
| 0 | 1 | 2 | a | 
| 3 | 4 | 5 | x | 
| 3 | 4 | 5 | y | 
| 3 | 4 | 5 | x | 
| 3 | 4 | 5 | z | 


| COL1 | COL2 | COL3 | COLX | 
----------------------------- 
| 0 | 1 | 2 | a | 
| 3 | 4 | 5 | x | 

O forse questo: http://www.sqlfiddle.com/#!3/af826/1

dati:

CREATE TABLE tbl 
    ([col1] int, [col2] int, [col3] int, [colX] varchar(1)); 

INSERT INTO tbl 
    ([col1], [col2], [col3], [colX]) 
VALUES 
    (0, 1, 2, 'a'), 
    (0, 1, 2, 'b'), 
    (0, 1, 2, 'c'), 
    (0, 1, 2, 'a'), 
    (0, 1, 3, 'a'), 
    (3, 4, 5, 'x'), 
    (3, 4, 5, 'y'), 
    (3, 4, 5, 'x'), 
    (3, 4, 5, 'z'); 

Soluzione:

select * from tbl; 


with a as 
(
    select row_number() over(partition by col1, col2, col3 order by colX) as rn 
    from tbl 
) 
delete from a where rn > 1; 

select * from tbl; 

uscita:

| COL1 | COL2 | COL3 | COLX | 
----------------------------- 
| 0 | 1 | 2 | a | 
| 0 | 1 | 2 | b | 
| 0 | 1 | 2 | c | 
| 0 | 1 | 2 | a | 
| 0 | 1 | 3 | a | 
| 3 | 4 | 5 | x | 
| 3 | 4 | 5 | y | 
| 3 | 4 | 5 | x | 
| 3 | 4 | 5 | z | 

| COL1 | COL2 | COL3 | COLX | 
----------------------------- 
| 0 | 1 | 2 | a | 
| 0 | 1 | 3 | a | 
| 3 | 4 | 5 | x | 
+0

ce l'ha fatta, grazie mille –

2

Vorrei suggerire di usare CTE e leggere tutti i record non-dup in una tabella separata se hai molti duplicati. Tuttavia, v'è un post consigliato da seguire: MSDN

+1

Sembra che tu abbia prima menzionato l'approccio sql "CTE", che è il più semplice e nella maggior parte degli scenari funziona. –

1

Supponendo colX è unico (che non è il caso nel tuo esempio, anche se lei ha detto "diverso colX") è possibile utilizzare le seguenti operazioni per eliminare i duplicati:

;with cteDuplicates as 
(
    select 
     *, 
     row_number() over (partition by col1, col2, col3 order by colX) as ID 
    from Duplicates 
) 
delete D from Duplicates D 
    inner join cteDuplicates C on C.colX = D.Colx 
where ID > 1 

(Diciamo che la vostra tabella è denominata "duplicati")

Se colX non è univoco, aggiungere una nuova colonna uniqueidentifier, inserire i valori distinti in esso e quindi utilizzare il codice di cui sopra unendo su quella colonna, invece di colX.

2

Se siete OK con solo mantenendo il valore minimo di colX, si può fare questo:

delete t from t inner join 
    (select min(colx) mincolx, col1, col2, col3 
    from t 
    group by col1, col2, col3 
    having count(1) > 1) as duplicates 
    on (duplicates.col1 = t.col1 
    and duplicates.col2 = t.col2 
    and duplicates.col3 = t.col3 
    and duplicates.mincolx <> t.colx) 

Il problema è che avete ancora le righe in cui tutte e quattro le colonne sono gli stessi. Per sbarazzarsi di questi, dopo aver eseguito la prima query, è necessario utilizzare una tabella temporanea.

SELECT distinct col1, col2, col3, colx 
INTO temp 
    FROM (SELECT col1, col2, col3 
     from t 
     group by col1, col2, col3 
     having count(1) > 1) subq; 

DELETE from t where exists 
    (select 1 from temp 
    where temp.col1 = t.col1 
     and temp.col2 = t.col2 
     and temp.col3 = t.col3); 

Here's an example SQLFiddle.

0

presumo si sta utilizzando SQL Server 2005/2008.

SELECT col1, 
     col2, 
     col3, 
     colx 
FROM 
    (SELECT *, 
      row_number() OVER (PARTITION BY col1,col2,col3 
          ORDER BY colx) AS r 
    FROM table_name) a 
WHERE r = 1; 
0

soluzione più semplice potrebbe essere la seguente supponiamo di avere tavolo emp_dept (EmpID, deptid), che ha le righe duplicate, Sul database Oracle

delete from emp_dept where exists (select * from emp_dept i where i.empid = emp_dept.empid and i.deptid = emp_dept.deptid and i.rowid < emp_dept.rowid) 

Su sql server o anydatabase che non supporta la funzione ID id di riga, è necessario aggiungere la colonna Identity solo per identificare ogni riga. dicono abbiamo aggiunto nid come identità al tavolo

alter table emp_dept add nid int identity(1,1) -- to add identity column 

ora query per eliminare duplicati potrebbe essere scritto come

delete from emp_dept where exists (select * from emp_dept i where i.empid = emp_dept.empid and i.deptid = emp_dept.deptid and i.nid< emp_dept.nid) 

Qui il concetto è eliminare tutte le righe per le quali esiste altre righe che hanno simili valori fondamentali ma rowid o identità più piccoli. Quindi se esistono righe duplicate, una di esse con ID o ID di riga superiore verrà eliminata. e per la riga non c'è duplicato non riesce a trovare id di riga inferiore quindi non verrà eliminato.

0

provare questo codice bt sul proprio rischio

Delete from Table_name 
WHERE Table_name.%%physloc%% 
     NOT IN (SELECT MAX(b.%%physloc%%) 
       FROM Table_name b 
       group by Col_1,Col_2) 

Secondo metodo utilizzando row_number() questo è metodo sicuro

WITH CTE_Dup AS 
(

SELECT * ROW_NUMBER()OVER (PARTITIONBY SalesOrderno, ItemNo ORDER BY SalesOrderno, ItemNo) 
AS ROW_NO 
from dbo.SalesOrderDetails 
) 
Delete FROM CTE_Dup;