Удалить строки с дубликатом первичного составного ключа

У меня есть таблица, состоящая из следующих целочисленных столбцов:

group_id, person_id, sequence

В идеале первичный ключ должен быть (group_id, person_id), но есть некоторые дубликаты, которые мне нужно удалить в первую очередь. Всякий раз, когда есть дубликаты, я хочу сохранить только самое низкое значение последовательности.

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

SELECT COUNT(*) AS num, group_id, person_id, MAX(sequence)
FROM my_table
GROUP BY group_id, person_id
HAVING COUNT(*) > 1;

Я уверен, что мне не хватает чего-то простого. Есть ли простой способ удалить эти дубликаты?

Благодарю.

2 ответа

Решение

Попробуйте написать запрос, который возвращает строки, которые вы хотите удалить. Предполагая, что комбинация (group_id,person_id,sequence) УНИКАЛЬНО, и у вас нет значений NULL...

 SELECT t.* 
   FROM my_table t
   JOIN ( SELECT o.group_id
               , o.person_id
               , MAX(o.sequence) AS max_sequence
            FROM my_table o
           GROUP BY o.group_id, o.person_id
          HAVING COUNT(*) > 1
        ) d
    ON d.group_id      = t.group_id
   AND d.person_id     = t.person_id
   AND d.max_sequence  = t.sequence

Мы можем преобразовать это в DELETE заявление, заменив SELECT Ключевое слово с DELETE ключевое слово.

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

Просто предшествуйте SELECT CREATE TABLE some_new_table_name AS,

Затем мы можем ссылаться на "сохраненные" строки в запросе DELETE.

DELETE t.*
  FROM my_table t
  JOIN some_new_table_name d
    ON d.group_id      = t.group_id
   AND d.person_id     = t.person_id
   AND d.max_sequence  = t.sequence

Этот подход получает только один из дубликатов. Если исходный запрос имеет значения счетчика больше 2, то нам нужно будет повторять это достаточное количество раз, каждый раз удаляя самое высокое значение последовательности, повторяя это до тех пор, пока значения счетчика не превысят 1.

Если нужно удалить много дубликатов, мы могли бы использовать немного другой шаблон, чтобы получить их одним махом.

Вместо возвращения MAX(sequence) (строка, которую мы хотим удалить), мы можем вместо этого вернуть MIN(sequence)Строка, которую мы хотим сохранить. И мы бы изменили предикат,

    AND d.max_sequence  = t.sequence

быть

    AND d.min_sequence  <> t.sequence

Так что мы удаляем все строки для этого group_id, person_id За исключением одного с минимальным значением.

Я настоятельно рекомендую вам написать это как SELECT прежде чем преобразовать его в DELETE заявление. И я также рекомендую иметь хорошую резервную копию таблицы и / или "сохранить" копии строк, которые вы собираетесь удалить. На всякий случай нужно восстановить несколько строк.

Все столбцы должны быть повторены. Так сгруппируйте, примените все столбцы, как это

select * from my_table where not EXISTS (
    SELECT group_id, person_id, min(sequence)
    FROM my_table
    GROUP BY group_id, person_id
    HAVING COUNT(*) > 1);
Другие вопросы по тегам