2009-07-24 24 views
6

Có ai có tuyên bố sql thanh lịch để xóa bản ghi trùng lặp khỏi bảng không, nhưng chỉ khi có nhiều hơn x số bản sao? Vì vậy, nó cho phép lên đến 2 hoặc 3 bản sao, nhưng đó là nó?Truy vấn SQL - Xóa các bản sao nếu có nhiều hơn 3 dups?

Hiện nay tôi có một tuyên bố chọn nào sau đây:

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 

này hoạt động tuyệt vời. Nhưng bây giờ những gì tôi muốn làm là chỉ xóa những hàng nếu họ có nhiều hơn nói 2 bản sao.

Cảm ơn

+0

khi có 5 bản sao, bạn có muốn duy nhất còn lại sau khi xóa, hoặc ba? – Stobor

Trả lời

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 

Truy vấn được sản xuất một 'số hàng' cho mỗi bản ghi, nhóm lại theo các (dupcol1, dupcol2) và ra lệnh bằng ID . Trong thực tế số hàng này đếm 'trùng lặp' có cùng một dupcol1 và dupcol2 và gán sau đó số 1, 2, 3 .. N, thứ tự theo ID. Nếu bạn muốn giữ lại chỉ 2 'bản sao', sau đó bạn cần phải xoá bỏ những người được chỉ định số 3,4,.. N và đó là phần được chăm sóc bởi các DELLETE.. WHERE rn > 2;

Sử dụng phương pháp này, bạn có thể thay đổi ORDER BY cho phù hợp với yêu thích của bạn đơn đặt hàng (ví dụ: ORDER BY ID DESC), để LATESTrn=1, thì bên cạnh mới nhất là rn = 2 v.v. Phần còn lại giữ nguyên, số DELETE sẽ chỉ xóa các số cũ nhất khi chúng có số hàng cao nhất.

Không giống như this closely related question, khi điều kiện trở nên phức tạp hơn, việc sử dụng CTE và row_number() trở nên đơn giản hơn. Hiệu suất có thể vẫn còn có vấn đề nếu không tồn tại chỉ mục truy cập thích hợp.

+0

Cảm ơn Remus, nhưng vì tôi không phải là chuyên gia sql và không quen thuộc với các từ khóa cụ thể từ năm 2005, bạn có thể giải thích cho tôi về truy vấn đang làm gì không? Tôi đoán phân vùng là một lối tắt đẹp cho việc nối trái với một bảng được nhóm, tương tự như ví dụ đầu tiên của tôi ?? Vì vậy, dòng thứ hai của bạn là trả lại một id mới của tất cả các bản ghi trùng lặp dựa trên các cột được cung cấp? Có phải rn số lần hàng đã được nhân đôi dựa trên các cột trong dòng thứ hai không? Cảm ơn. – Scott

+1

Truy vấn đang sản xuất 'số hàng' cho mỗi bản ghi, được nhóm theo (dupcol1, dupcol2) và được sắp xếp theo ID. Trong thực tế số hàng này đếm 'trùng lặp' có cùng một dupcol1 và dupcol2 và gán sau đó số 1, 2, 3 .. N, thứ tự theo ID. Nếu bạn muốn giữ chỉ 2 'trùng lặp', sau đó bạn cần phải xóa những người đã được giao các số 3,4, .. N và đó là một phần được chăm sóc bởi DELLETE .. WHERE rn> 2; HTH, hãy cho tôi biết nếu vẫn chưa rõ ràng. –

+0

Không, tôi hiểu, cảm ơn rất nhiều. Một điều cuối cùng, tôi muốn đảm bảo rằng tôi luôn giữ kỷ lục MỚI NHẤT. Vì vậy, nếu tôi giữ hồ sơ với nói <2 bản sao, và sau đó ném ra tất cả những người khác, làm thế nào tôi có thể sửa đổi các truy vấn để đảm bảo rằng gần đây nhất (tối đa (id)) hai hoặc ba hồ sơ của bảng được bảo tồn. Ví dụ: giả sử một bản ghi được liệt kê trong hệ thống của chúng tôi 10 lần. Điều này vi phạm quy tắc trùng lặp "2". Chúng tôi muốn xóa 7 bản sao, chỉ để lại một bản ghi chính và hai bản sao. Theo hồ sơ chủ, chúng tôi có nghĩa là bản ghi MỚI nhất (cập nhật nhất) đã đi vào hệ thống. – Scott

3

HAVING là bạn của bạn

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

0

Khá muộn nhưng giải pháp đơn giản có thể như sau giả sử chúng tôi có bảng emp_dept (empid, deptid) có hàng trùng lặp, Ở đây tôi đã sử dụng @Count là varibale .. ví dụ: 2 nhân đôi cho phép sau đó @count = 2 On Oracle cơ sở dữ liệu

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) 

Trên máy chủ sql hoặc anydatabase mà không hỗ trợ hàng id kinda đặc trưng, ​​chúng ta cần thêm cột sắc chỉ để xác định mỗi hàng. nói, chúng tôi đã thêm vào nid như danh tính vào bảng

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

tại truy vấn để xóa trùng lặp có thể được viết như

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) 

Ở đây khái niệm này là xóa tất cả các hàng mà tồn tại hàng khác có tương tự giá trị cốt lõi nhưng n hoặc số lượng lớn hơn của hàng hoặc bản sắc nhỏ hơn. Do đó nếu có hàng trùng lặp thì một hàng có id hàng cao hơn hoặc nhận dạng sẽ bị xóa. và đối với hàng không có trùng lặp nó không tìm thấy id hàng thấp hơn do đó sẽ không bị xóa.

0

Đối với 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); 
Các vấn đề liên quan