Xóa các hàng trùng lặp khỏi bảng trong Oracle


151

Tôi đang thử nghiệm một cái gì đó trong Oracle và điền vào một bảng với một số dữ liệu mẫu, nhưng trong quá trình tôi vô tình tải các bản ghi trùng lặp, vì vậy bây giờ tôi không thể tạo khóa chính bằng cách sử dụng một số cột.

Làm cách nào tôi có thể xóa tất cả các hàng trùng lặp và chỉ để lại một trong số chúng?

Câu trả lời:


306

Sử dụng rowidbút danh.

DELETE FROM your_table
WHERE rowid not in
(SELECT MIN(rowid)
FROM your_table
GROUP BY column1, column2, column3);

Ở đâu column1, column2column3tạo nên khóa nhận dạng cho mỗi bản ghi. Bạn có thể liệt kê tất cả các cột của bạn.


6
+1 Tôi phải tìm hai số điện thoại trùng lặp được chôn trong hơn 12.000 hồ sơ. Đã thay đổi XÓA thành CHỌN và điều này tìm thấy chúng trong vài giây. Tiết kiệm cho tôi rất nhiều thời gian, cảm ơn bạn.
shimonyk

3
Cách tiếp cận này không hiệu quả với tôi. Tôi không biết tại sao. Khi tôi thay thế "XÓA" bằng "CHỌN *", nó trả về các hàng tôi muốn xóa, nhưng khi tôi thực hiện bằng "XÓA" thì nó chỉ bị treo vô thời hạn.
aro_biz

Của tôi cũng là treo hoặc chỉ thực hiện rất lâu. Đã chạy được khoảng 22 giờ và vẫn đi. Bảng có 21M hồ sơ.
Cameron Castillo

Tôi đề nghị thêm bộ lọc vào câu lệnh WHERE nếu bạn có bộ dữ liệu rất lớn và nếu khả thi, điều này có thể giúp mọi người với các truy vấn chạy dài.
Ricardo Sanchez

2
Nếu lựa chọn hoạt động, nhưng xóa không, đó có thể là do kích thước của truy vấn con kết quả. Điều thú vị là trước tiên hãy tạo một bảng tạo với kết quả truy vấn con, xây dựng một chỉ mục trên cột min (rowid) và sau đó chạy câu lệnh xóa.
Wouter

15

Từ hỏi Tom

delete from t
 where rowid IN ( select rid
                    from (select rowid rid, 
                                 row_number() over (partition by 
                         companyid, agentid, class , status, terminationdate
                                   order by rowid) rn
                            from t)
                   where rn <> 1);

(đã sửa lỗi ngoặc đơn bị thiếu)


Dấu ngoặc đơn bị thiếu trong tuyên bố. Tôi cho rằng nó nên ở cuối?
Cameron Castillo

12

Từ DevX.com :

DELETE FROM our_table
WHERE rowid not in
(SELECT MIN(rowid)
FROM our_table
GROUP BY column1, column2, column3...) ;

Trong đó cột1, cột2, v.v. là khóa bạn muốn sử dụng.


12
DELETE FROM tablename a
      WHERE a.ROWID > ANY (SELECT b.ROWID
                             FROM tablename b
                            WHERE a.fieldname = b.fieldname
                              AND a.fieldname2 = b.fieldname2)

1
Nhận xét của tôi ở trên về câu trả lời được bình chọn hàng đầu, chính yêu cầu này đã thực sự giải quyết vấn đề của tôi.
aro_biz

2
Điều này sẽ - rất nhiều - chậm hơn trên các bảng lớn so với giải pháp của Bill.
Wouter

8

Giải pháp 1)

delete from emp
where rowid not in
(select max(rowid) from emp group by empno);

Giải pháp 2)

delete from emp where rowid in
               (
                 select rid from
                  (
                    select rowid rid,
                      row_number() over(partition by empno order by empno) rn
                      from emp
                  )
                where rn > 1
               );

Giải pháp 3)

delete from emp e1
         where rowid not in
          (select max(rowid) from emp e2
           where e1.empno = e2.empno ); 

6

tạo bảng t2 khi chọn phân biệt * từ t1;


không phải là một câu trả lời - distinct *sẽ lấy mọi bản ghi khác nhau ít nhất 1 ký hiệu trong 1 cột. Tất cả những gì bạn cần là chỉ chọn các giá trị riêng biệt từ các cột bạn muốn tạo khóa chính - Câu trả lời của Bill là ví dụ tuyệt vời về phương pháp này.
Nogard

1
Đó là những gì tôi cần (loại bỏ các dòng hoàn toàn giống hệt nhau). Cảm ơn !
Emmanuel

Một nhược điểm khác của phương pháp này là bạn phải tạo một bản sao của bảng. Đối với các bảng lớn, điều này ngụ ý cung cấp thêm không gian bảng và xóa hoặc thu hẹp không gian bảng sau khi sao chép. Phương pháp của Bill có nhiều lợi ích hơn và không có nhược điểm nào thêm.
Wouter

3

Bạn nên thực hiện một khối pl / sql nhỏ bằng cách sử dụng một con trỏ cho vòng lặp và xóa các hàng bạn không muốn giữ. Ví dụ:

declare
prev_var my_table.var1%TYPE;

begin

for t in (select var1 from my_table order by var 1) LOOP

-- if previous var equal current var, delete the row, else keep on going.
end loop;

end;

Tôi tin rằng downvote là bởi vì bạn đang sử dụng PL / SQL khi bạn có thể làm điều đó trong SQL, khiến bạn đang tự hỏi.
Thế chiến.

7
Chỉ vì bạn có thể làm điều đó trong SQL, không có nghĩa là giải pháp duy nhất của nó. Tôi đã đăng giải pháp này, sau khi tôi thấy giải pháp chỉ dành cho SQL. Tôi nghĩ rằng phiếu bầu là cho câu trả lời không chính xác.
Nick

3

Để chọn các mục trùng lặp, chỉ có thể định dạng truy vấn:

SELECT GroupFunction(column1), GroupFunction(column2),..., 
COUNT(column1), column1, column2...
FROM our_table
GROUP BY column1, column2, column3...
HAVING COUNT(column1) > 1

Vì vậy, truy vấn chính xác theo đề nghị khác là:

DELETE FROM tablename a
      WHERE a.ROWID > ANY (SELECT b.ROWID
                             FROM tablename b
                            WHERE a.fieldname = b.fieldname
                              AND a.fieldname2 = b.fieldname2
                              AND ....so on.. to identify the duplicate rows....)

Truy vấn này sẽ giữ bản ghi cũ nhất trong cơ sở dữ liệu cho các tiêu chí được chọn trong WHERE CLAUSE.

Hiệp hội chứng nhận Oracle (2008)


2

Cách nhanh nhất cho các bàn thực sự lớn

  1. Tạo bảng ngoại lệ với cấu trúc bên dưới: exceptions_table

    ROW_ID ROWID
    OWNER VARCHAR2(30)
    TABLE_NAME VARCHAR2(30)
    CONSTRAINT VARCHAR2(30)
  2. Hãy thử tạo một ràng buộc duy nhất hoặc khóa chính sẽ bị vi phạm bởi các bản sao. Bạn sẽ nhận được một thông báo lỗi vì bạn có bản sao. Bảng ngoại lệ sẽ chứa các hàng cho các hàng trùng lặp.

    alter table add constraint
    unique --or primary key
    (dupfield1,dupfield2) exceptions into exceptions_table;
  3. Tham gia bảng của bạn với exceptions_table bằng rowid và xóa dups

    delete original_dups where rowid in (select ROW_ID from exceptions_table);
  4. Nếu số lượng hàng cần xóa là lớn, thì hãy tạo một bảng mới (với tất cả các khoản trợ cấp và chỉ mục) chống liên kết với exceptions_table bằng rowid và đổi tên bảng gốc thành bảng gốc_dups và đổi tên new_table_with_no_dups thành bảng gốc

    create table new_table_with_no_dups AS (
        select field1, field2 ........ 
        from original_dups t1
        where not exists ( select null from exceptions_table T2 where t1.rowid = t2.row_id )
    )

2

Sử dụng rowid-

delete from emp
 where rowid not in
 (select max(rowid) from emp group by empno);

Sử dụng tự tham gia-

delete from emp e1
 where rowid not in
 (select max(rowid) from emp e2
 where e1.empno = e2.empno );

Xin chào Tandale, Vui lòng sử dụng công cụ định dạng mã trong khi gửi câu trả lời vì nó làm tăng khả năng đọc.
NSNoob

2

Giải pháp 4)

 delete from emp where rowid in
            (
             select rid from
                (
                  select rowid rid,
                  dense_rank() over(partition by empno order by rowid
                ) rn
             from emp
            )
 where rn > 1
);

Bạn có thể giải thích một chút?
Dieter Meemken

xếp hạng dày đặc với phân vùng bằng cách xếp hạng cho các hàng trùng lặp có cùng số, ví dụ ba hàng có thứ hạng 1, 1, 1 và tạo hàng cho mỗi hàng là unic và chúng tôi đang cố gắng xóa các hàng không khớp.
DoOrDie

chúng ta có thể sử dụng cả hai hàm tier và dense_rank nhưng tôi nghĩ thứ hạng hoạt động hoàn hảo trong kịch bản này.
DoOrDie

2

1. giải pháp

delete from emp
    where rowid not in
    (select max(rowid) from emp group by empno);

2. sloution

delete from emp where rowid in
               (
                 select rid from
                  (
                    select rowid rid,
                      row_number() over(partition by empno order by empno) rn
                      from emp
                  )
                where rn > 1
               );

3. giải quyết

delete from emp e1
         where rowid not in
          (select max(rowid) from emp e2
           where e1.empno = e2.empno ); 

4. giải pháp

 delete from emp where rowid in
            (
             select rid from
                (
                  select rowid rid,
                  dense_rank() over(partition by empno order by rowid
                ) rn
             from emp
            )
 where rn > 1
);

2

5. giải pháp

delete from emp where rowid in 
    (
      select  rid from
       (
         select rowid rid,rank() over (partition by emp_id order by rowid)rn from emp     
       )
     where rn > 1
    );

2
DELETE from table_name where rowid not in (select min(rowid) FROM table_name group by column_name);

và bạn cũng có thể xóa các bản ghi trùng lặp theo cách khác

DELETE from table_name a where rowid > (select min(rowid) FROM table_name b where a.column=b.column);

2
create table abcd(id number(10),name varchar2(20))

insert into abcd values(1,'abc')

insert into abcd values(2,'pqr')


insert into abcd values(3,'xyz')

insert into abcd values(1,'abc')

insert into abcd values(2,'pqr')

insert into abcd values(3,'xyz')


select * from abcd
id  Name
1   abc
2   pqr
3   xyz
1   abc
2   pqr
3   xyz

Delete Duplicate record but keep Distinct Record in table 

DELETE 
FROM abcd a
WHERE ROWID > (SELECT MIN(ROWID) FROM abcd b
WHERE b.id=a.id
);

run the above query 3 rows delete 

select * from abcd

id  Name 
1   abc
2   pqr
3   xyz

1
DELETE FROM tableName  WHERE ROWID NOT IN (SELECT   MIN (ROWID) FROM table GROUP BY columnname);

Câu trả lời tương tự như câu trả lời công phu hơn của Bill the Lizard.
Wouter

1
delete from dept
where rowid in (
     select rowid
     from dept
     minus
     select max(rowid)
     from dept
     group by DEPTNO, DNAME, LOC
);

Bạn có thể thêm thông tin về cách của bạn? Cảm ơn.
Phóng viên

1

Để có hiệu suất tốt nhất, đây là những gì tôi đã viết:
(xem kế hoạch thực hiện)

DELETE FROM your_table
WHERE rowid IN 
  (select t1.rowid from your_table  t1
      LEFT OUTER JOIN (
      SELECT MIN(rowid) as rowid, column1,column2, column3
      FROM your_table 
      GROUP BY column1, column2, column3
  )  co1 ON (t1.rowid = co1.rowid)
  WHERE co1.rowid IS NULL
);

1

Kiểm tra các tập lệnh dưới đây -

1.

Create table test(id int,sal int); 

2.

    insert into test values(1,100);    
    insert into test values(1,100);    
    insert into test values(2,200);    
    insert into test values(2,200);    
    insert into test values(3,300);    
    insert into test values(3,300);    
    commit;

3.

 select * from test;    

Bạn sẽ thấy ở đây 6-hồ sơ.
4. bên dưới truy vấn -

delete from 
   test
where rowid in
 (select rowid from 
   (select 
     rowid,
     row_number()
    over 
     (partition by id order by sal) dup
    from test)
  where dup > 1)
  1. select * from test;

Bạn sẽ thấy rằng các bản ghi trùng lặp đã bị xóa.
Hy vọng điều này giải quyết truy vấn của bạn. Cảm ơn :)


1

Tôi không thấy bất kỳ câu trả lời nào sử dụng các biểu thức bảng và hàm cửa sổ phổ biến. Đây là những gì tôi thấy dễ dàng nhất để làm việc với.

DELETE FROM
 YourTable
WHERE
 ROWID IN
    (WITH Duplicates
          AS (SELECT
               ROWID RID, 
               ROW_NUMBER() 
               OVER(
               PARTITION BY First_Name, Last_Name, Birth_Date)
                  AS RN
               SUM(1)
               OVER(
               PARTITION BY First_Name, Last_Name, Birth_Date
               ORDER BY ROWID ROWS BETWEEN UNBOUNDED PRECEDING 
                                       AND UNBOUNDED FOLLOWING)
                   AS CNT
              FROM
               YourTable
              WHERE
               Load_Date IS NULL)
     SELECT
      RID
     FROM
      duplicates
     WHERE
      RN > 1);

Những điều cần lưu ý:

1) Chúng tôi chỉ kiểm tra sự trùng lặp trên các trường trong mệnh đề phân vùng.

2) Nếu bạn có một số lý do để chọn một bản sao so với các mục khác, bạn có thể sử dụng một mệnh đề theo mệnh đề để tạo hàng đó sẽ có row_number () = 1

3) Bạn có thể thay đổi số trùng lặp được bảo toàn bằng cách thay đổi mệnh đề where thành "Trường hợp RN> N" với N> = 1 (Tôi đã nghĩ N = 0 sẽ xóa tất cả các hàng có trùng lặp, nhưng nó sẽ chỉ xóa tất cả các hàng) .

4) Đã thêm trường phân vùng Sum, truy vấn CTE sẽ gắn thẻ mỗi hàng với các hàng số trong nhóm. Vì vậy, để chọn các hàng có trùng lặp, bao gồm mục đầu tiên sử dụng "WHERE cnt> 1".


0
create or replace procedure delete_duplicate_enq as
    cursor c1 is
    select *
    from enquiry;
begin
    for z in c1 loop
        delete enquiry
        where enquiry.enquiryno = z.enquiryno
        and rowid > any
        (select rowid
        from enquiry
        where enquiry.enquiryno = z.enquiryno);
    end loop;
 end delete_duplicate_enq;

Một nhược điểm lớn của phương pháp này là sự tham gia bên trong. Đối với các bảng lớn, điều này sẽ chậm hơn rất nhiều so với phương pháp của Bill. Ngoài ra, sử dụng PL / SQL để làm điều này là quá mức cần thiết, bạn cũng có thể sử dụng điều này bằng cách sử dụng sql.
Wouter

0

giải pháp :

delete from emp where rowid in
(
    select rid from
    (
        select rowid rid,
        row_number() over(partition by empno order by empno) rn
        from emp
    )
    where rn > 1
);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.