2009-07-24 67 views
6

有沒有人有一個優雅的sql語句來刪除表中的重複記錄,但只有有超過x的重複數?所以它最多允許2或3個重複,但就是這樣?SQL查詢 - 如果超過3個dups,刪除重複項?

目前,我有一個SELECT語句執行以下操作:

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 

這個偉大的工程。但現在我想要做的只是刪除那些行,如果他們不止說2個重複。

感謝

+0

時,有5次重複,你只需要一個左側的刪除後,或三個? – Stobor 2009-07-24 01:02:06

回答

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 

查詢是製造每個記錄的 '行號',由(dupcol1,dupcol2)分組和編號排序。實際上,這個行號計算具有相同的dupcol1和dupcol2的「重複」,然後分配數字1,2,3 ...... N,按ID排序。如果你想保持僅有2「重複」,那麼你需要刪除那些被分配到的號碼3,4,.. N,這是由DELLETE.. WHERE rn > 2;

使用此方法的照顧的一部分,你可以改變ORDER BY以滿足您的首選訂單(例如ORDER BY ID DESC),以便LATEST具有rn=1,那麼最近的下一個是rn = 2等等。其餘保持不變,DELETE將只刪除最舊的那些,因爲它們具有最高的行數。

this closely related question不同,隨着條件變得更復雜,使用CTE和row_number()變得更簡單。如果沒有適當的訪問索引存在,性能可能會有問題。

+0

感謝Remus,但由於我不是SQL專家,也不像2005年特定的關鍵字那麼熟悉,您能向我解釋查詢的內容嗎? 我認爲分區是一個很好的快捷方式左加入到一個分組表,類似於我的第一個例子?所以你的第二行是根據提供的列返回所有重複記錄的新ID? 是根據第二行中的列重複該行的次數? 謝謝。 – Scott 2009-07-24 01:43:15

3

HAVING是你的朋友

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

0

相當晚,但最簡單的解決方案可以是如下 假設我們有表刪除emp_dept(EMPID,DEPTID),其具有重複的行, 這裏我已經使用作爲@count .. varibale例如2允許複製,然後2 @count = 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) 

上的SQL Server或anydatabase不支持行ID還挺功能,我們需要添加標識列只是爲了識別每一行。 說,我們增加了NID身份表

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

現在查詢刪除重複可以寫成

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) 

這裏的概念是刪除所有行對其存在具有類似其他行核心價值觀但n或更小數量的較小rowid或身份。因此,如果存在重複的行,那麼具有較高行ID或標識的行將被刪除。對於行沒有重複它找不到更低的行ID因此不會被刪除。

0

對於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);