Видалення повторюваних рядків із таблиці в 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 і це знайшло їх за лічені секунди. Врятували мені тону часу, дякую.
шимок

3
Такий підхід для мене не спрацював. Я не знаю чому. Коли я замінив "DELETE" на "SELECT *", він повернув рядки, які я хотів видалити, але коли я виконав "DELETE", він просто зависав нескінченно.
aro_biz

Шахта також або висить, або просто виконується надзвичайно довго. Біг близько 22 годин і все ще їде. Таблиця має 21М записів.
Камерон Кастільо

Я пропоную додати подальшу фільтрацію до оператора WHERE, якщо у вас дуже великий набір даних, і якщо це можливо, це може допомогти людям із тривалими запитами.
Рікардо Санчес

2
Якщо вибір працює, але видалення цього не відбувається, це може бути пов’язано з розміром отриманого запиту. Спочатку може бути цікаво створити таблицю створення з результатом запиту, побудувати індекс на стовпці min (rowid), а потім запустити оператор delete.
Wouter

14

З запитання Тома

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
Парентез відсутній у твердженні. Я припускаю, що це має бути наприкінці?
Камерон Кастільо

12

Від DevX.com :

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

Там, де стовбур1, стовпчик2 тощо є ключем, який ви хочете використовувати.


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
Це буде набагато повільніше на величезних столах, ніж рішення Білла.
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 як виділити відмінне * від t1;


не відповідь - distinct *візьме кожен запис, який відрізняється принаймні 1 символом в 1 стовпчику. Все, що вам потрібно, це вибирати окремі значення лише з стовпців, для яких потрібно зробити первинні ключі - відповідь Білла - чудовий приклад такого підходу.
Nogard

1
Це було те, що мені було потрібно (видалити абсолютно однакові рядки). Дякую !
Еммануїл

Ще один недолік цього методу полягає в тому, що вам потрібно створити копію таблиці. Для величезних таблиць це означає надання додаткового простору таблиць та видалення або зменшення простору таблиць після копіювання. Метод Білла має більше переваг і додаткових недоліків.
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;

Я вважаю, що скорочення полягає в тому, що ви використовуєте PL / SQL, коли можете це зробити в SQL, якщо вам цікаво.
ВВ.

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 (2008)


2

Найшвидший спосіб дійсно великих таблиць

  1. Створіть таблицю винятків зі структурою нижче: exceptions_table

    ROW_ID ROWID
    OWNER VARCHAR2(30)
    TABLE_NAME VARCHAR2(30)
    CONSTRAINT VARCHAR2(30)
  2. Спробуйте створити унікальне обмеження або первинний ключ, який буде порушений дублікатами. Ви отримаєте повідомлення про помилку, оскільки у вас є дублікати. Таблиця винятків міститиме рядки для повторюваних рядків.

    alter table add constraint
    unique --or primary key
    (dupfield1,dupfield2) exceptions into exceptions_table;
  3. Приєднуйтесь до своєї таблиці за допомогою таблиці exceptions_table за рядком та видаляйте дупси

    delete original_dups where rowid in (select ROW_ID from exceptions_table);
  4. Якщо кількість рядків для видалення є великим, то створіть нову таблицю (з усіма грантами та індексами), що не поєднує винятків з таблицею 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

Використання рядка-

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

Ви можете трохи пояснити?
Дітер Мемкен

щільний ранг з розділом дає ранг для повторюваних рядків з однаковим числом, наприклад, три рядки, що мають 1, 1, 1 і rowid, створюють для кожного ряду, як unic, і ми намагаємось видалити ті рядки, які не відповідають.
DoOrDie

ми можемо використовувати як рангові, так і щільні_rank функції, але я думаю, що ранг відмінно працює в цьому сценарії.
DoOrDie

2

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

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

Така ж відповідь, як і більш ретельна відповідь Білла Ящера.
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) Ви можете змінити дублікат числа, який зберігається, змінивши пункт final, де пункт "Де 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;

Основним недоліком цього методу є внутрішнє з'єднання. Для великих таблиць це буде набагато повільніше, ніж метод Білла. Крім того, використання 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
);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.