2009-07-24 25 views
6

Czy ktoś ma elegancki komunikat sql, aby usunąć duplikaty rekordów z tabeli, ale tylko wtedy, gdy jest więcej niż x liczba duplikatów? Pozwala to na 2 lub 3 duplikaty, ale to wszystko?Zapytanie SQL - usuwanie duplikatów w przypadku więcej niż 3 duplikatów?

Obecnie mam select który wykonuje następujące czynności:

delete table 
from table t 
left outer join (
select max(id) as rowid, dupcol1, dupcol2 
from table 
group by dupcol1, dupcol2 
) as keeprows on t.id=keeprows.rowid 
where keeprows.rowid is null 

to działa świetnie. Ale teraz chciałbym tylko usunąć te wiersze, jeśli mają więcej niż powiedzenie 2 duplikatów.

Dzięki

+0

gdy istnieje 5 duplikaty, chcesz tylko jeden po usunięciu, czy trzy? – Stobor

Odpowiedz

7
with cte as (
    select row_number() over (partition by dupcol1, dupcol2 order by ID) as rn 
    from table) 
delete from cte 
    where rn > 2; -- or >3 etc 

Zapytanie jest wytwarzania 'numer wiersza' dla każdego rekordu, pogrupowanych przez (dupcol1, dupcol2) i uporządkowaną przez ID . W efekcie ten numer wiersza zlicza "duplikaty", które mają ten sam dupcol1 i dupcol2 i przydziela wtedy liczbę 1, 2, 3 .. N, porządek według ID. Jeśli chcesz zachować zaledwie 2 'duplikaty, a następnie trzeba usunąć te, które zostały przypisane numery 3,4,.. N i to jest część załatwione przez DELLETE.. WHERE rn > 2;

Stosując tę ​​metodę można zmienić ORDER BY dostosowane preferowany kolejność (np. ORDER BY ID DESC), tak aby LATEST ma rn=1, następnie następny do najnowszego jest rn = 2 i tak dalej. Reszta pozostaje taka sama, DELETE usunie tylko najstarsze, ponieważ mają najwyższe numery wierszy.

W przeciwieństwie do this closely related question, ponieważ stan staje się bardziej złożony, użycie CTE i row_number() staje się prostsze. Wydajność może być problematyczna, jeśli nie istnieje odpowiedni indeks dostępu.

+0

Dzięki Remus, ale ponieważ nie jestem ekspertem w dziedzinie SQL i nie jestem tak dobrze zaznajomiony z konkretnymi słowami kluczowymi 2005, czy mógłbyś mi wyjaśnić, co robi zapytanie? Domyślam się, że partycja jest dobrym skrótem do łączenia w lewo do zgrupowanego stołu, podobnego do mojego pierwszego przykładu? Więc twoja druga linia zwraca nowy identyfikator wszystkich duplikatów na podstawie podanych kolumn? Czy liczba powtórzeń wiersza została powtórzona na podstawie kolumn w drugim wierszu? Dzięki. – Scott

+1

Zapytanie tworzy "numer wiersza" dla każdego rekordu, pogrupowane według (dupcol1, dupcol2) i uporządkowane według identyfikatora. W efekcie ten numer wiersza zlicza "duplikaty", które mają ten sam dupcol1 i dupcol2 i przydziela wtedy liczbę 1, 2, 3 .. N, porządek według ID. Jeśli chcesz zachować tylko 2 "duplikaty", musisz usunąć te, którym przypisano numery 3,4, ... N i to jest część zaopiekowana przez DELLETE .. WHERE rn> 2; HTH, daj mi znać, jeśli nadal nie jest jasne. –

+0

Nie, muszę, bardzo dziękuję. Ostatnią rzeczą jednak jest zapewnienie, że zawsze zachowuję NAJNOWSZY rekord. Tak więc, jeśli zachowam rekordy z powiedzeniem <2 duplikatów, a następnie wyrzucam wszystkie pozostałe, w jaki sposób mogę zmodyfikować zapytanie, aby zapewnić zachowanie najnowszych (max (id)) dwóch lub trzech rekordów tabeli. Jako przykład: powiedz jeden rekord jest wymieniony w naszym systemie 10 razy. Narusza to zasadę "2". Chcielibyśmy usunąć 7 z duplikatów, pozostawiając tylko jeden rekord główny i dwa duplikaty. Według rekordu głównego mamy na myśli NAJNOWSZĄ (najbardziej aktualną) płytę, która trafiła do systemu. – Scott

3

HAVING jest twoim przyjacielem

select id, count(*) cnt from table group by id having cnt>2

0

dość późnym ale Najprostsze rozwiązanie może być następujący załóżmy, że posiada stół emp_dept (EmpID, deptid), który ma powtarzające się wierszy, Mam tu stosowane @Count jak varibale .. np 2 powielony pozwolono następnie @count = 2 Na bazie danych Oracle

delete from emp_dept where @Count <= (select count(1) from emp_dept i where i.empid = emp_dept.empid and i.deptid = emp_dept.deptid and i.rowid < emp_dept.rowid) 

na serwerze SQL lub anydatabase która nie obsługuje wiersz id trochę funkcji, musimy dodać kolumnę tożsamości tylko do identyfikacji każdego wiersza. powiedzieć dodaliśmy nid jako tożsamości do stołu

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

teraz zapytanie do usuwania zduplikowanych można zapisać jako

delete from emp_dept where @@Count <= (select count(1) from emp_dept i where i.empid = emp_dept.empid and i.deptid = emp_dept.deptid and i.nid< emp_dept.nid) 

Tutaj koncepcja jest usunąć wszystkie wiersze, dla których istnieje inne wiersze, które mają podobne podstawowe wartości, ale n lub większa liczba mniejszych wierszy lub tożsamości. Dlatego też, jeśli istnieją zduplikowane wiersze, to jeden, który ma wyższy identyfikator wiersza lub tożsamość zostanie usunięty. a dla wiersza nie ma duplikatu, nie udaje mu się znaleźć id w dolnym wierszu, więc nie zostanie usunięty.

0

Dla Oracle:

delete from test where rowid = ANY (select min(test.rowid) from test left 
    outer join 
    (select min(rowid) row_id from test group by id,name)t on 
    test.rowid=t.row_id where t.row_id is null group by test.id,test.name); 
Powiązane problemy