2011-07-05 103 views
40

Tôi có một bảng trong cơ sở dữ liệu PostgreSQL 8.3.8, không có khóa/ràng buộc trên đó và có nhiều hàng với cùng giá trị giống nhau.Xóa bản ghi trùng lặp trong PostgreSQL

Tôi muốn xóa tất cả các bản sao và chỉ giữ lại 1 bản sao của mỗi hàng.

Có một cột cụ thể (có tên là "khóa") có thể được sử dụng để xác định các từ khóa trùng lặp (nghĩa là chỉ tồn tại một mục nhập cho mỗi "khóa" riêng biệt).

Tôi làm cách nào để thực hiện việc này? (lý tưởng với một lệnh SQL) Tốc độ không phải là vấn đề trong trường hợp này (chỉ có một vài hàng).

Trả lời

39
DELETE FROM dupes a 
WHERE a.ctid <> (SELECT min(b.ctid) 
       FROM dupes b 
       WHERE a.key = b.key); 
+0

Hoàn hảo, cảm ơn! Tôi không biết về ctid –

+4

Không sử dụng nó, nó quá chậm! –

+2

Trong khi giải pháp này chắc chắn hoạt động, [giải pháp dưới đây] của @rapimo (https://stackoverflow.com/a/12963112/1156554) thực hiện nhanh hơn nhiều. Tôi tin rằng điều này đã làm với câu lệnh chọn bên trong ở đây được thực hiện N lần (cho tất cả N hàng trong bảng dupes) chứ không phải là nhóm đang diễn ra trong giải pháp khác. – David

4

Tôi sẽ sử dụng một bảng tạm thời:

create table tab_temp as 
select distinct f1, f2, f3, fn 
    from tab; 

Sau đó, xóa và đổi tên tabtab_temp vào tab.

+4

Cách tiếp cận này không chiếm trigger, chỉ mục và thống kê. Chắc chắn bạn có thể thêm chúng, nhưng nó cũng bổ sung thêm nhiều công việc nữa. – Jordan

+0

Không phải ai cũng cần điều đó. Cách tiếp cận này là cực kỳ nhanh và làm việc tốt hơn nhiều so với phần còn lại trên 200k email (varchar 250) mà không có chỉ mục. –

58

Một giải pháp nhanh hơn là

DELETE FROM dups a USING (
     SELECT MIN(ctid) as ctid, key 
     FROM dups 
     GROUP BY key HAVING COUNT(*) > 1 
    ) b 
     WHERE a.key = b.key 
     AND a.ctid <> b.ctid 
+14

Tại sao nó nhanh hơn giải pháp của a_horse_with_no_name? – Roberto

+1

Điều này được thực hiện trong một giây trong thử nghiệm của tôi, so với 53 phút cho giải pháp được chấp nhận. – Alex

+0

Điều này thực sự nhanh hơn! – Alfabravo

5

tôi đã phải tạo ra phiên bản của riêng tôi. Phiên bản được viết bởi @a_horse_with_no_name là cách quá chậm trên bảng của tôi (hàng 21M). Và @rapimo chỉ đơn giản là không xóa dups.

Dưới đây là những gì tôi sử dụng trên PostgreSQL 9,5

DELETE FROM your_table 
WHERE ctid IN (
    SELECT unnest(array_remove(all_ctids, actid)) 
    FROM (
     SELECT 
      min(b.ctid)  AS actid, 
      array_agg(ctid) AS all_ctids 
     FROM your_table b 
     GROUP BY key1, key2, key3, key4 
     HAVING count(*) > 1) c); 
11

Tôi cố gắng này:

DELETE FROM tablename 
WHERE id IN (SELECT id 
       FROM (SELECT id, 
          ROW_NUMBER() OVER (partition BY column1, column2, column3 ORDER BY id) AS rnum 
        FROM tablename) t 
       WHERE t.rnum > 1); 

cung cấp bởi Postgres wiki:

https://wiki.postgresql.org/wiki/Deleting_duplicates

+0

Bất kỳ ý tưởng nào về hiệu suất so với câu trả lời của @ rapimo và câu trả lời được chấp nhận (@a_horse_with_no_name)? – tuxayo

+1

Điều này sẽ không hoạt động nếu, giống như các câu hỏi nói, _all_ cột giống nhau, 'id' được bao gồm. – ibizaman

0

này làm việc tốt cho tôi. Tôi đã có một bảng, các thuật ngữ, có chứa các giá trị trùng lặp. Đã chạy truy vấn để điền bảng tạm thời với tất cả các hàng trùng lặp. Sau đó, tôi chạy một tuyên bố xóa với những id trong bảng tạm thời. giá trị là cột chứa các bản sao.

 CREATE TEMP TABLE dupids AS 
     select id from (
        select value, id, row_number() 
over (partition by value order by value) 
    as rownum from terms 
       ) tmp 
        where rownum >= 2; 

delete from [table] where id in (select id from dupids) 
-2

việc cho cả SQL NORMAL và PostgreSQL (cũng hoạt động trong AWS dịch chuyển đỏ)

DROP TABLE IF EXISTS backupOfTheTableContainingDuplicates; 

CREATE TABLE aNewEmptyTemporaryOrBackupTable 
AS SELECT DISTINCT * FROM originalTableContainingDuplicates; 

TRUNCATE TABLE originalTableContainingDuplicates; 

INSERT INTO originalTableContainingDuplicates SELECT * FROM 
aNewEmptyTemporaryOrBackupTable ; 

DROP TABLE aNewEmptyTemporaryOrBackupTable ; 

GIẢI THÍCH TRÊN SQL SCRIPT

Vì vậy,

The 1st truy vấn đảm bảo, Nếu bạn có bất kỳ bảng sao lưu/tạm thời nào của bảng gốc chứa trong g bản sao sau đó đầu tiên thả bảng đó.

Truy vấn thứ hai, tạo bảng mới (Tạm thời/Sao lưu) với các mục nhập duy nhất trong Bảng gốc có chứa bản sao, vì vậy bảng tạm thời mới giống với bảng gốc MINUS mục trùng lặp.

Truy vấn thứ 3, cắt bớt hoặc làm trống bảng gốc.

Truy vấn thứ 4, chèn hoặc sao chép tất cả các mục nhập duy nhất trong bảng tạm thời vào bảng gốc đã bị cắt ngắn gần đây (Vì vậy, không có dữ liệu). Sau khi truy vấn này được thực hiện, Bảng gốc sẽ được điền dữ liệu UNIQUE trong bảng tạm thời.

Truy vấn thứ năm, xóa/xóa bảng tạm thời không cần thiết.

Vì vậy, Kết quả cuối cùng là, bảng gốc chỉ có ĐƠN VỊ UNIQUE và không trùng lặp.

5

này là nhanh chóng và ngắn gọn:

DELETE FROM dupes T1 
    USING dupes T2 
WHERE T1.ctid < T2.ctid -- delete the older versions 
    AND T1.key = T2.key; -- add more columns if needed 
+0

điều này thật tuyệt vời! – user151496

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