2010-08-17 39 views
6

Sau khi tìm kiếm stackoverflow.com Tôi đã tìm thấy một số câu hỏi yêu cầu cách xóa các bản sao, nhưng không ai trong số chúng giải quyết được tốc độ.Kỹ thuật nhanh nhất để xóa dữ liệu trùng lặp

Trong trường hợp của tôi, tôi có một bảng có 10 cột chứa 5 triệu bản sao hàng chính xác. Ngoài ra, tôi có ít nhất một triệu hàng khác với các bản sao trong 9 trong số 10 cột. Kỹ thuật hiện tại của tôi đang thực hiện (cho đến nay) 3 giờ để xóa 5 triệu hàng này. Đây là quá trình của tôi:

-- Step 1: **This step took 13 minutes.** Insert only one of the n duplicate rows into a temp table 
select 
    MAX(prikey) as MaxPriKey, -- identity(1, 1) 
    a, 
    b, 
    c, 
    d, 
    e, 
    f, 
    g, 
    h, 
    i 
into #dupTemp 
FROM sourceTable 
group by 
    a, 
    b, 
    c, 
    d, 
    e, 
    f, 
    g, 
    h, 
    i 
having COUNT(*) > 1 

Tiếp theo,

-- Step 2: **This step is taking the 3+ hours** 
-- delete the row when all the non-unique columns are the same (duplicates) and 
-- have a smaller prikey not equal to the max prikey 
delete 
from sourceTable 
from sourceTable 
inner join #dupTemp on 
    sourceTable.a = #dupTemp.a and 
    sourceTable.b = #dupTemp.b and 
    sourceTable.c = #dupTemp.c and 
    sourceTable.d = #dupTemp.d and 
    sourceTable.e = #dupTemp.e and 
    sourceTable.f = #dupTemp.f and 
    sourceTable.g = #dupTemp.g and 
    sourceTable.h = #dupTemp.h and 
    sourceTable.i = #dupTemp.i and 
    sourceTable.PriKey != #dupTemp.MaxPriKey 

Bất cứ lời khuyên về cách để tăng tốc độ này lên, hoặc một cách nhanh hơn? Hãy nhớ rằng tôi sẽ phải chạy lại cho các hàng không trùng lặp chính xác.

Cảm ơn rất nhiều.

CẬP NHẬT:
Tôi phải dừng bước 2 khi chạy ở mốc 9 giờ. Tôi đã thử phương pháp OMG Ponies và kết thúc chỉ sau 40 phút. Tôi đã thử bước 2 của mình với xóa hàng loạt của Andomar, nó chạy 9 giờ trước khi tôi dừng nó lại. CẬP NHẬT: Chạy một truy vấn tương tự với một trường ít hơn để loại bỏ một tập bản sao khác và truy vấn chỉ chạy trong 4 phút (8000 hàng) bằng phương pháp OMG Ponies.

Tôi sẽ thử kỹ thuật cte cơ hội tiếp theo tôi nhận được, tuy nhiên, tôi nghi ngờ phương pháp OMG Ponies 'sẽ khó đánh bại.

+1

Một vài cách tối ưu hóa dễ dàng các truy vấn của bạn ở trên - bạn không cần phải có, b, c vv trong 'SELECT' của truy vấn hàng đầu - bạn chỉ cần PriKey và thả HAVING - rồi , trong truy vấn thứ hai chỉ 'DELETE FROM sourceTable WHERE PriKey NOT IN (CHỌN DT.MaxPriKey FROM #dupTemp DT)' –

+0

Cảm ơn bạn đã có mẹo. –

Trả lời

4

gì về EXISTS:

DELETE FROM sourceTable 
WHERE EXISTS(SELECT NULL 
       FROM #dupTemp dt 
       WHERE sourceTable.a = dt.a 
       AND sourceTable.b = dt.b 
       AND sourceTable.c = dt.c 
       AND sourceTable.d = dt.d 
       AND sourceTable.e = dt.e 
       AND sourceTable.f = dt.f 
       AND sourceTable.g = dt.g 
       AND sourceTable.h = dt.h 
       AND sourceTable.i = dt.i 
       AND sourceTable.PriKey < dt.MaxPriKey) 
+0

Hãy giải thích lý do bạn cho rằng cách này sẽ nhanh hơn. –

+1

@ sub13: EXISTS khác với JOIN hoặc IN - nó trả về giá trị đúng trong lần khớp đầu tiên của tiêu chí. Lý thuyết là ít công việc nên bằng một truy vấn nhanh hơn. Trên một lưu ý liên quan, [bài viết này] (http://explainextended.com/2009/09/15/not-in-vs-not-exists-vs-left-join-is-null-sql-server/) sẽ giải thích và tương phản một số tùy chọn. –

+0

Tất cả các cột trong EXISTS() có cần phải là không null không? –

0

nhiều Vâng điều differnt. Đầu tiên sẽ giống như công việc này (làm một lựa chọn o đảm bảo, thậm chí có thể đưa vào một bảng tạm thời của riêng của nó, #recordsToDelete):

delete 
from sourceTable 
left join #dupTemp on 
     sourceTable.PriKey = #dupTemp.MaxPriKey 
where #dupTemp.MaxPriKey is null 

Tiếp theo bạn có thể bảng chỉ số tạm thời, đặt một chỉ mục trên prikey

Nếu bạn có bản ghi trong bảng tạm thời của bảng bạn muốn xóa, bạn có thể xóa theo lô thường nhanh hơn việc khóa toàn bộ bảng bằng cách xóa.

+0

Khi xử lý các cột không null, 'NOT IN' và' NOT EXISTS' hiệu quả hơn: http://explainextended.com/2009/09/15/not-in-vs-not-exists-vs-left- join-is-null-sql-server/ –

3

Nút cổ chai trong xóa hàng loạt thường là giao dịch mà SQL Server phải xây dựng. Bạn có thể tăng tốc độ đáng kể bằng cách chia tách các giao dịch nhỏ hơn. Ví dụ: để xóa 100 hàng tại một thời điểm:

while 1=1 
    begin 

    delete top 100 
    from sourceTable 
    ... 

    if @@rowcount = 0 
     break 
    end 
+0

Đó là một ý tưởng rất thú vị. Tôi chắc chắn sẽ thử điều này. –

+0

BTW: Tôi không nghĩ rằng xóa top 100 là cú pháp hợp lệ –

+2

@ subt13: Đó là - xem [SQL Server 2008 BOL - DELETE] (http://msdn.microsoft.com/en-us/library/ms189835.aspx) –

4

Bạn có thể đủ khả năng để bảng ban đầu không khả dụng trong một thời gian ngắn không?

Tôi nghĩ giải pháp nhanh nhất là tạo bảng mới mà không có bản sao. Về cơ bản phương pháp tiếp cận mà bạn sử dụng với bảng tạm thời, nhưng tạo ra một bảng "thường xuyên" thay thế.

Sau đó thả bảng gốc và đổi tên bảng trung gian để có cùng tên với bảng cũ.

+0

Có. Bảng thông thường có nhanh hơn bảng tạm hay thứ gì đó không? Xin hãy tha thứ cho sự thiếu hiểu biết của tôi :) –

+0

Có lẽ sẽ là giải pháp nhanh nhất được đề xuất từ ​​trước đến nay - nếu có chìa khóa nước ngoài, vv… đau đớn và dễ bị lỗi nếu bạn không cẩn thận, nhưng chắc chắn đáng xem xét. –

+1

@ subt13: bạn cần bảng thông thường vì bạn sẽ giữ nó;) (ngược lại với bảng tạm thời của bạn) @WillA: vâng bạn đúng, người ta cần phải cẩn thận với những ràng buộc. –

0

Đây là phiên bản mà bạn có thể kết hợp cả hai bước thành một bước.

WITH cte AS 
    (SELECT prikey, ROW_NUMBER() OVER (PARTITION BY a,b,c,d,e,f,g,h,i ORDER BY 
     prikey DESC) AS sequence 
    FROM sourceTable 
    ) 

DELETE 
FROM sourceTable 
WHERE prikey IN 
    (SELECT prikey 
    FROM cte 
    WHERE sequence > 1 
    ) ; 

Nhân tiện, bạn có bất kỳ chỉ mục nào có thể bị xóa tạm thời không?

+1

Martin Smith cho thấy ngày khác rằng CTE có thể được tham chiếu như là nguồn DELETE, hoạt động giống như một khung nhìn cập nhật. –

+0

Ya, đây là một tính năng tuyệt vời mà tôi không chắc chắn về hiệu quả so với bảng #temp thời trang cũ. Phải mất một lúc để làm bất cứ điều gì trên nhiều hàng này. Tôi có một chỉ số nhóm. Nếu cần nhiều hơn, tôi chắc chắn có thể thêm chúng. –

1

... dựa trên OMG Ponies nhận xét ở trên, phương pháp CTE gọn hơn một chút. Phương thức này làm việc kỳ diệu trên các bảng mà bạn đã (vì bất kỳ lý do gì) không có khóa chính - nơi bạn có thể có các hàng giống hệt nhau trên tất cả các cột.

;WITH cte AS (
SELECT ROW_NUMBER() OVER 
      (PARTITION BY a,b,c,d,e,f,g,h,i ORDER BY prikey DESC) AS sequence 
    FROM sourceTable 
) 
DELETE 
FROM cte 
WHERE sequence > 1 
+0

Tuyệt. Tôi nghĩ rằng tôi đã giúp đỡ, và cuối cùng tôi nhận được sự giúp đỡ. Đây là một biểu diễn tốt hơn so với đề nghị của tôi. – bobs

+0

Điều này rất nhỏ gọn, nhưng tôi quan tâm hơn đến tốc độ. Từ những gì tôi đã đọc và nhìn thấy với ctes, chúng chỉ đơn thuần là đường hợp lý trong trường hợp của tôi. Tuy nhiên, hãy sửa tôi nếu tôi sai. –

+0

@ subt13: Bạn sẽ phải cho chúng tôi biết sau khi so sánh kế hoạch truy vấn thực tế giữa các tùy chọn khác nhau. –

Các vấn đề liên quan