16 января 2020

Удаление дубликатов строк SQL запросом

    Иногда в базах данных встречаются таблицы без первичного ключа. Это позволяет пользователям вносить в подобные таблицы данные содержащие полные дубликаты строк. Что делать, если перед вами стоит задача почистить эту таблицу от дубликатов?
    Создадим тестовую таблицу и заполним ее данными, содержащими дубликаты строк:
CREATE TABLE test
(
  id INTEGER,
  name VARCHAR(100)
);
INSERT INTO test(id, name) VALUES (1, 'Вера');
INSERT INTO test(id, name) VALUES (2, 'Надежда');
INSERT INTO test(id, name) VALUES (1, 'Вера');
INSERT INTO test(id, name) VALUES (3, 'Любовь');
INSERT INTO test(id, name) VALUES (1, 'Вера');
В исходном состоянии таблица "test" содержит пять строк, две из которых - лишние:
id name
1 Вера
2 Надежда
1 Вера
3 Любовь
1 Вера

    PostgresSQL легко позволяет решить эту проблему с помощью скрытых системных столбцов, которые создаются в каждой таблице самой СУБД. Первым и самым очевидным кандидатом является известный всем системный столбец "OID" (Object Identifier):
DELETE FROM test 
WHERE oid NOT IN (SELECT MIN(oid) 
                    FROM test 
                   GROUP BY test.*);
В результате выполнения этого запроса все дубликаты из таблицы исчезли, и в таблице мы видим только уникальные строки:
id name
1 Вера
2 Надежда
3 Любовь

Метод работает, но имеет недостатки:
  1-й - большой. В пользовательские таблицы столбец "OID" добавляется, только если при создании таблицы указывается параметр "WITH OIDS" или включен параметр конфигурации "default_with_oids". Т.е. столбца "OID" в таблице может и не быть!
  2-й - небольшой и маловероятный. Столбец "OID" реализован как 32-х битное целое число. В больших или долгоживущих базах данных этот счетчик может пойти по кругу, тем самым существует вероятность, что значение этого столбца в одной таблице может повториться.

    Альтернативой столбцу "OID" является другое системное поле - "CTID", которое идентифицирует физическое расположение строки в таблице. Оно присутствует в таблице всегда и гарантированно является уникальным. Возвращаем таблицу "test" в исходное состояние и выполняем запрос:
DELETE FROM test 
WHERE ctid NOT IN (SELECT MIN(ctid) 
                     FROM test 
                   GROUP BY test.*);
В результате выполнения этого запроса в таблице "test" остались только три уникальные строки.

P.S. При использовании данного способа удаления дубликатов строк в таблице, необходимо учитывать, что на большой таблице, этот запрос потребует очень много ресурсов и может тормознуть сервер.

2 комментария:

  1. Анонимный24 ноября, 2021 10:55

    А если на уникальность надо проверить не все поля, а только часть?

    ОтветитьУдалить
    Ответы
    1. Попробуйте так:
      DELETE FROM test
      WHERE ctid NOT IN (SELECT MIN(ctid)
      FROM test
      GROUP BY field1, field2, field10);

      Удалить