Удаление повторяющихся строк из таблицы в Oracle

Я тестирую что-то в Oracle и заполняю таблицу некоторыми образцами данных, но в процессе я случайно загрузил повторяющиеся записи, поэтому теперь я не могу создать первичный ключ, используя некоторые столбцы.

Как удалить все повторяющиеся строки и оставить только одну из них?


person juan    schedule 09.02.2009    source источник


Ответы (22)


Используйте псевдоколонку rowid.

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

Где column1, column2 и column3 составляют идентификационный ключ для каждой записи. Вы можете перечислить все свои столбцы.

person Bill the Lizard    schedule 09.02.2009
comment
+1 Мне пришлось найти два повторяющихся телефонных номера, закопанных в более чем 12 000 записей. Изменил DELETE на SELECT, и это нашло их за секунды. Сэкономил мне массу времени, спасибо. - person shimonyk; 23.09.2010
comment
Этот подход у меня не сработал. Не знаю почему. Когда я заменил DELETE на SELECT *, он вернул строки, которые я хотел удалить, но когда я выполнил с помощью DELETE, он просто зависал на неопределенное время. - person aro_biz; 25.06.2012
comment
Моя тоже либо висит, либо просто работает очень долго. Бегала около 22 часов и все еще еду. В таблице 21M записей. - person Cameron Castillo; 22.08.2013
comment
Я предлагаю добавить дополнительную фильтрацию к оператору WHERE, если у вас очень большой набор данных и, если возможно, это может помочь людям с длительными запросами. - person Ricardo Sanchez; 08.04.2014
comment
Если выбор работает, а удаление нет, это может быть связано с размером результирующего подзапроса. Может быть интересно сначала создать таблицу с результатом подзапроса, построить индекс по столбцу min (rowid), а затем запустить оператор удаления. - person Wouter; 15.05.2014
comment
Разве это не удалит только первый дубликат, если их несколько? - person Darrel Lee; 11.01.2018

Из Спросите Тома

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

(исправлены недостающие круглые скобки)

person Dead Programmer    schedule 18.03.2011
comment
В заявлении отсутствуют круглые скобки. Полагаю, это должно быть в конце? - person Cameron Castillo; 22.08.2013

С DevX.com:

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

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

person Mark    schedule 09.02.2009

Решение 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 ); 
person DoOrDie    schedule 31.12.2015
comment
Не могли бы вы рассказать нам о плюсах и минусах каждого подхода? - person Arun Gowda; 26.08.2020

создать таблицу t2 как выбрать отличную * от t1;

person Mohammed khaled    schedule 11.01.2013
comment
не ответ - distinct * будет брать каждую запись, которая отличается хотя бы 1 символом в 1 столбце. Все, что вам нужно, это выбрать отдельные значения только из столбцов, которые вы хотите сделать первичными ключами - ответ Билла - отличный пример такого подхода. - person Nogard; 11.01.2013
comment
Это было то, что мне было нужно (удалить полностью идентичные строки). Спасибо ! - person Emmanuel; 20.02.2013
comment
Еще одним недостатком этого метода является то, что вам нужно создать копию своей таблицы. Для огромных таблиц это подразумевает предоставление дополнительного табличного пространства, а также удаление или сжатие табличного пространства после копирования. У метода Билла больше преимуществ, но нет дополнительных недостатков. - person Wouter; 15.05.2014

Вы должны создать небольшой блок 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;
person Nick    schedule 09.02.2009
comment
Я считаю, что отрицательное голосование вызвано тем, что вы используете PL / SQL, когда можете делать это в SQL, если вам интересно. - person WW.; 10.02.2009
comment
То, что вы можете сделать это в SQL, не означает, что это единственное решение. Я опубликовал это решение после того, как увидел решение только для SQL. Я думал, что голоса были за неправильные ответы. - person Nick; 10.02.2009

Для выбора дубликатов можно использовать только формат запроса:

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

person user1799846    schedule 17.06.2014

Самый быстрый способ для действительно больших столов

  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 по 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 )
    )
    
person user2158672    schedule 30.05.2014

Используя 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 );
person Dnyaneshwar Tandale    schedule 28.12.2015
comment
Привет, Тандейл, пожалуйста, используйте инструмент форматирования кода при отправке ответов, так как он повышает удобочитаемость. - person NSNoob; 28.12.2015

Решение 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
);
person DoOrDie    schedule 31.12.2015
comment
Вы можете немного объяснить? - person Dieter Meemken; 31.12.2015
comment
Плотный ранг с разделением по дает ранг для повторяющихся строк с одинаковым номером, например, три строки с рангом 1, 1, 1 и rowid создают для каждой строки как unic, и мы пытаемся удалить те rowid, которые не совпадают. - person DoOrDie; 31.12.2015
comment
мы можем использовать как функции rank, так и elastic_rank, но я думаю, что rank отлично работает в этом сценарии. - person DoOrDie; 31.12.2015

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-е решение

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
);
person Community    schedule 10.02.2016

5. решение

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
    );
person DoOrDie    schedule 10.02.2016

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);
person Md Wasi    schedule 07.01.2017

Вот что я написал для лучшей производительности:
(см. План выполнения)

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
);
person Enguerrand JORE    schedule 11.03.2015

Проверьте ниже скрипты -

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;

Вы увидите, что повторяющиеся записи были удалены.
Надеюсь, это решит ваш запрос. Спасибо :)

person Rakesh Roshan    schedule 14.09.2017

Я не видел ответов, в которых используются общие табличные выражения и оконные функции. Это то, с чем мне легче всего работать.

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) Добавлено поле Sum partition для запроса CTE, которое помечает каждую строку числом строк в группе. Поэтому для выбора строк с дубликатами, включая первый элемент, используйте «WHERE cnt> 1».

person Darrel Lee    schedule 01.11.2017

Это сообщение в блоге было действительно полезно для общих случаев:

Если строки полностью дублированы (все значения во всех столбцах могут иметь копии), столбцов для использования нет! Но чтобы сохранить его, вам все равно понадобится уникальный идентификатор для каждой строки в каждой группе. К счастью, в Oracle уже есть кое-что, что вы можете использовать. Гребец. У всех строк в Oracle есть rowid. Это физический локатор. То есть он указывает, где на диске Oracle хранит строку. Это уникально для каждой строки. Таким образом, вы можете использовать это значение для идентификации и удаления копий. Для этого замените min () на min (rowid) в некоррелированном удалении:

delete films
where  rowid not in (
  select min(rowid)
  from   films
  group  by title, uk_release_date
)
person Ala Abid    schedule 07.06.2021

решение :

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
);
person sandeep gupta    schedule 28.06.2019

person    schedule
comment
Повторите мой комментарий выше к ответу, получившему наибольшее количество голосов, именно этот запрос фактически решил мою проблему. - person aro_biz; 25.06.2012
comment
На огромных столах это будет намного медленнее, чем решение Билла. - person Wouter; 15.05.2014

person    schedule
comment
Тот же ответ, что и более подробный ответ Ящера Билла. - person Wouter; 15.05.2014

person    schedule
comment
Вы можете добавить больше информации о своем пути? Спасибо. - person Reporter; 20.05.2014

person    schedule
comment
Главный недостаток этого метода - внутреннее соединение. Для больших таблиц это будет намного медленнее, чем метод Билла. Кроме того, использование PL / SQL для этого является излишним, вы также можете использовать это, просто используя sql. - person Wouter; 15.05.2014