Удаление повторяющихся строк из таблицы в 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 часов и все еще еду. Таблица имеет 21M записей.
Кэмерон Кастильо

Я предлагаю добавить дополнительную фильтрацию в оператор 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...) ;

Где column1, column2 и т. Д. - это ключ, который вы хотите использовать.


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
На огромных столах это будет намного медленнее, чем решение Билла.
Воутер

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 столбце. Все, что вам нужно, это выбрать отдельные значения только из столбцов, которые вы хотите сделать первичными ключами - ответ Билла является отличным примером такого подхода.
Ногард

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, если вам интересно.
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. Попробуйте создать уникальное ограничение или первичный ключ, который будет нарушен дубликатами. Вы получите сообщение об ошибке, потому что у вас есть дубликаты. Таблица исключений будет содержать строки для повторяющихся строк.

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

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

Можешь немного объяснить?
Дитер Мимкен

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

мы можем использовать функции и ранга, и плотного_ранка, но я думаю, что ранг отлично работает в этом сценарии.
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);

Тот же ответ, что и более сложный ответ Билла Ящерицы.
Wouter

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

Можете ли вы добавить больше информации о вашем пути? Спасибо.
Репортер

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) Если у вас есть причина выбрать один дубликат среди других, вы можете использовать предложение order by, чтобы в этой строке было row_number () = 1

3) Вы можете изменить сохраненный дубликат числа, изменив конечное условие where на «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;

Основным недостатком этого метода является внутреннее соединение. Для больших таблиц это будет намного медленнее, чем метод Билла. Кроме того, использование 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.