March 29, 2023

Простой запрос на удаление дубликатов из таблицы

В этой небольшой статье я хочу поделиться простым способом удаления дубликатов из таблицы. Запрос работает в базах данных MySQL, MariaDB и PostgreSQL. Если вам интересен такой запрос для других СУБД, напишите мне в комментариях.
Давайте начнем. Предположим, у нас есть простая таблица с двумя столбцами: id - это первичный ключ и v простое целочисленное значение:

create table t (
    id int primary key,
    v int
);

insert into t(id, v) values 
(1, 1),(2, 1),(3, 2),(4, 2),(5, 1),(6, 1),
(7, 2),(8, 3),(9, 2),(10, 4),(11, 3);

Приведенный выше код создает таблицу и вставляет несколько значений. Как видите, id имеет уникальные значения, но поле v имеет несколько дубликатов:

+====+===+
| id | v |
+====+===+
| 1  | 1 |
| 2  | 1 |
| 3  | 2 |
| 4  | 2 |
| 5  | 1 |
| 6  | 1 |
| 7  | 2 |
| 8  | 3 |
| 9  | 2 |
| 10 | 4 |
| 11 | 3 |
+----+---+

Наша миссия состоит в том, чтобы удалить эти повторяющиеся строки в столбце v и сохранить уникальные значения с минимальным значением идентификатора.
Как мы можем найти дубликаты? Мы можем использовать простое LEFT JOIN для поля v с дополнительным условием для предотвращения объединения строк:

select * 
from t
left join t t1 on t.v = t1.v and t.id > t1.id;

Запрос дает нам следующий результат:

+====+===+========+========+
| id | v | id     | v      |
+====+===+========+========+
| 1  | 1 | (null) | (null) |
| 2  | 1 | 1      | 1      |
| 3  | 2 | (null) | (null) |
| 4  | 2 | 3      | 2      |
| 5  | 1 | 1      | 1      |
| 5  | 1 | 2      | 1      |
| 6  | 1 | 1      | 1      |
| 6  | 1 | 2      | 1      |
| 6  | 1 | 5      | 1      |
| 7  | 2 | 3      | 2      |
| 7  | 2 | 4      | 2      |
| 8  | 3 | (null) | (null) |
| 9  | 2 | 3      | 2      |
| 9  | 2 | 4      | 2      |
| 9  | 2 | 7      | 2      |
| 10 | 4 | (null) | (null) |
| 11 | 3 | 8      | 3      |
+----+---+--------+--------+

Мы видим, что уникальные строки с минимальным идентификатором имеют (нулевые) значения в последних столбцах. Итак, нам нужно удалить остальные. Мы можем сделать это в следующем простом запросе:

delete t.*
from t
left join t t1 on t.v = t1.v and t.id > t1.id
where t1.id is not null;

Просто запустите на SQLize.online и посмотрите результат

P.S. После этой публикации мой коллега @Akina предложил более короткую версию:

delete t.*
from t
join t t1 on t.v = t1.v and t.id > t1.id;