การลบแถวที่ซ้ำกันออกจากตารางใน Oracle


151

ฉันกำลังทดสอบบางอย่างใน Oracle และเติมตารางด้วยข้อมูลตัวอย่างบางส่วน แต่ในกระบวนการฉันโหลดระเบียนที่ซ้ำกันโดยไม่ได้ตั้งใจดังนั้นตอนนี้ฉันไม่สามารถสร้างคีย์หลักโดยใช้คอลัมน์บางคอลัมน์ได้

ฉันจะลบแถวที่ซ้ำกันทั้งหมดและปล่อยแถวเดียวได้อย่างไร

คำตอบ:


306

ใช้rowidนามแฝง

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

ที่ไหนcolumn1, column2และcolumn3ทำขึ้นที่สำคัญระบุสำหรับแต่ละระเบียน คุณอาจแสดงรายการคอลัมน์ทั้งหมดของคุณ


6
+1 ฉันต้องค้นหาหมายเลขโทรศัพท์ที่ซ้ำกันสองหมายเลขฝังใน 12,000 รายการ เปลี่ยน DELETE เป็น SELECT และพบได้ในไม่กี่วินาที ช่วยฉันสักหน่อยขอบคุณ
shimonyk

3
วิธีนี้ไม่ได้ผลสำหรับฉัน ฉันไม่รู้ว่าทำไม เมื่อฉันแทนที่ "DELETE" ด้วย "SELECT *" มันกลับแถวที่ฉันต้องการลบ แต่เมื่อฉันดำเนินการด้วย "DELETE" มันก็แค่ห้อยไปเรื่อย ๆ
aro_biz

ฉันก็ยังแขวนหรือดำเนินการนานมาก ทำงานเป็นเวลาประมาณ 22 ชั่วโมงและยังคงดำเนินต่อไป ตารางมีเร็กคอร์ด 21M
Cameron Castillo

ฉันขอแนะนำให้เพิ่มการกรองเพิ่มเติมไปยังคำสั่ง WHERE หากคุณมีชุดข้อมูลขนาดใหญ่มากและหากเป็นไปได้สิ่งนี้อาจช่วยให้ผู้ใช้ที่ใช้แบบสอบถามยาว
Ricardo Sanchez

2
หากการเลือกทำงานได้ แต่การลบไม่ได้อาจเป็นเพราะขนาดของแบบสอบถามย่อยที่เกิดขึ้น อาจน่าสนใจที่จะสร้างตารางด้วยผลลัพธ์แบบสอบถามย่อยก่อนสร้างดัชนีในคอลัมน์ min (rowid) จากนั้นเรียกใช้คำสั่ง delete
Wouter

14

จากAsk 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);

(แก้ไขวงเล็บที่หายไป)


1
ไม่มีวงเล็บในคำสั่ง ฉันคิดว่ามันควรจะเป็นที่สุด?
Cameron Castillo


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
ความคิดเห็นของฉันข้างต้นอีกครั้งบนคำตอบที่ได้รับคะแนนสูงสุดมันเป็นคำขอนี้ที่แก้ไขปัญหาของฉันได้จริง
aro_biz

2
นี่จะช้ากว่ามากบนโต๊ะขนาดใหญ่กว่าโซลูชันของ Bill
Wouter

8

โซลูชัน 1)

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

โซลูชัน 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
               );

โซลูชัน 3)

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

6

สร้างตาราง t2 เป็น select * ที่แตกต่างกันจาก t1;


ไม่ใช่คำตอบ - distinct *จะใช้ทุกระเบียนที่แตกต่างกันอย่างน้อย 1 สัญลักษณ์ใน 1 คอลัมน์ สิ่งที่คุณต้องทำก็คือเลือกค่าที่แตกต่างจากคอลัมน์ที่คุณต้องการสร้างคีย์หลัก - คำตอบของบิลเป็นตัวอย่างที่ดีของวิธีการนี้
Nogard

1
นั่นคือสิ่งที่ฉันต้องการ (ลบบรรทัดที่เหมือนกันทั้งหมด) ขอบคุณมาก!
Emmanuel

ข้อเสียของวิธีนี้ก็คือคุณต้องสร้างสำเนาของตารางของคุณ สำหรับตารางขนาดใหญ่สิ่งนี้แสดงถึงการให้พื้นที่ตารางเพิ่มเติมและการลบหรือลดขนาดพื้นที่ตารางหลังจากคัดลอก วิธีการเรียกเก็บเงินมีประโยชน์มากขึ้นและไม่มีข้อเสียเพิ่มเติม
Wouter

3

คุณควรทำบล็อก pl / sql ขนาดเล็กโดยใช้เคอร์เซอร์เพื่อวนซ้ำและลบแถวที่คุณไม่ต้องการเก็บไว้ ตัวอย่างเช่น

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;

ฉันเชื่อว่า downvote นั้นเป็นเพราะคุณใช้ PL / SQL เมื่อคุณสามารถทำได้ใน SQL ในกรณีที่คุณสงสัย
WW

7
เพียงเพราะคุณสามารถทำได้ใน SQL ไม่ได้หมายความว่ามันเป็นทางออกเดียว ฉันโพสต์โซลูชันนี้หลังจากฉันเห็นโซลูชัน SQL เท่านั้น ฉันคิดว่าการลงคะแนนเสียงนั้นเป็นคำตอบที่ไม่ถูกต้อง
นิค

3

หากต้องการเลือกรายการซ้ำเท่านั้นรูปแบบแบบสอบถามสามารถ:

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

ดังนั้นแบบสอบถามที่ถูกต้องตามข้อเสนอแนะอื่น ๆ คือ:

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....)

WHERE CLAUSEแบบสอบถามนี้จะเก็บบันทึกที่เก่าแก่ที่สุดในฐานข้อมูลสำหรับเกณฑ์ที่เลือกไว้ใน

Oracle Certified Associate (2008)


2

วิธีที่เร็วที่สุดสำหรับตารางที่ใหญ่จริงๆ

  1. สร้างตารางข้อยกเว้นที่มีโครงสร้างด้านล่าง: exceptions_table

    ROW_ID ROWID
    OWNER VARCHAR2(30)
    TABLE_NAME VARCHAR2(30)
    CONSTRAINT VARCHAR2(30)
  2. ลองสร้างข้อ จำกัด ที่ไม่ซ้ำกันหรือคีย์หลักซึ่งจะถูกละเมิดโดยซ้ำกัน คุณจะได้รับข้อความแสดงข้อผิดพลาดเนื่องจากคุณซ้ำซ้อน ตารางข้อยกเว้นจะมี rowids สำหรับแถวที่ซ้ำกัน

    alter table add constraint
    unique --or primary key
    (dupfield1,dupfield2) exceptions into exceptions_table;
  3. เข้าร่วมตารางของคุณด้วย exceptions_table โดย rowid และลบ dups

    delete original_dups where rowid in (select ROW_ID from exceptions_table);
  4. หากจำนวนแถวที่จะลบมีขนาดใหญ่ให้สร้างตารางใหม่ (พร้อมกับสิทธิ์ทั้งหมดและดัชนี) การต่อต้านการเข้าร่วมกับ Exceptions_table โดย rowid และเปลี่ยนชื่อตารางต้นฉบับเป็นตาราง original_dups และเปลี่ยนชื่อ new_table_with_no_dups เป็นตารางเดิม

    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

ใช้ rowid-

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

ใช้ตัวเองเข้าร่วม -

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

สวัสดี Tandale โปรดใช้เครื่องมือการจัดรูปแบบรหัสในขณะที่ส่งคำตอบเนื่องจากจะเพิ่มความสามารถในการอ่าน
NSNoob

2

โซลูชัน 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
);

คุณช่วยอธิบายหน่อยได้ไหม?
Dieter Meemken

อันดับหนาแน่นกับพาร์ติชันโดยให้อันดับสำหรับแถวที่ซ้ำกันด้วยหมายเลขเดียวกันเช่นสามแถวที่มีอันดับ 1, 1, 1 และ rowid สร้างสำหรับทุกแถวเป็น unic และเรากำลังพยายามลบ rowids เหล่านั้นที่ไม่ตรงกัน
DoOrDie

เราสามารถใช้ทั้งอันดับและฟังก์ชั่น dense_rank แต่ฉันคิดว่าอันดับทำงานได้อย่างสมบูรณ์ในสถานการณ์นี้
DoOrDie

2

1. การแก้ปัญหา

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.solution

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

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


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

และคุณยังสามารถลบระเบียนที่ซ้ำกันด้วยวิธีอื่น

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

คำตอบเดียวกับคำตอบที่ซับซ้อนยิ่งขึ้นของ Bill the Lizard
Wouter


1

เพื่อประสิทธิภาพที่ดีที่สุดนี่คือสิ่งที่ฉันเขียน:
(ดูแผนปฏิบัติการ)

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

ตรวจสอบสคริปต์ด้านล่าง -

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;    

คุณจะเห็น 6 ระเบียนที่นี่
4. เรียกใช้แบบสอบถามด้านล่าง -

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;

คุณจะเห็นว่ามีการลบระเบียนที่ซ้ำกัน
หวังว่านี่จะแก้ปัญหาการค้นหาของคุณ ขอบคุณ :)


1

ฉันไม่เห็นคำตอบใด ๆ ที่ใช้นิพจน์ตารางทั่วไปและฟังก์ชันหน้าต่าง นี่คือสิ่งที่ฉันคิดว่าง่ายที่สุดที่จะทำงานด้วย

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

สิ่งที่ควรทราบ:

1) เรากำลังตรวจสอบการทำซ้ำในฟิลด์ในข้อพาร์ติชันเท่านั้น

2) หากคุณมีเหตุผลบางอย่างที่จะเลือกสำเนาที่ซ้ำกันเหนือรายการอื่นคุณสามารถใช้คำสั่งซื้อโดยข้อเพื่อให้แถวนั้นจะมี row_number () = 1

3) คุณสามารถเปลี่ยนหมายเลขที่ซ้ำกันที่เก็บรักษาไว้โดยการเปลี่ยนสุดท้ายโดยใช้คำสั่ง "Where RN> N" ด้วย N> = 1 (ฉันคิดว่า N = 0 จะลบแถวทั้งหมดที่มีรายการซ้ำ แต่มันจะลบแถวทั้งหมด) .

4) เพิ่มเขตข้อมูลผลรวมของการสืบค้น CTE ซึ่งจะติดแท็กแต่ละแถวด้วยแถวจำนวนในกลุ่ม ดังนั้นในการเลือกแถวที่มีรายการซ้ำรวมถึงรายการแรกให้ใช้ "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;

ข้อเสียที่สำคัญของวิธีนี้คือการเข้าร่วมภายใน สำหรับตารางขนาดใหญ่จะช้ากว่าวิธีของ Bill มาก นอกจากนี้การใช้ PL / SQL เพื่อทำสิ่งนี้เกินความจริงคุณสามารถใช้สิ่งนี้ได้โดยการใช้ sql
Wouter

0

สารละลาย :

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
);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.