SQL — Удаление последних повторяющихся записей

#sql #oracle #greatest-n-per-group #sql-delete

Вопрос:

У меня есть таблица Order в моей базе данных Oracle. Каким-то образом задание вставки выполнялось дважды и создавало дубликаты записей с разницей только во временной метке:

ID Имя пункт Дата
1 Рик картофель 06-07-21 12:35:27.048000000 ВЕЧЕРА
2 Рик картофель 06-07-21 12:35:27.048100000 ВЕЧЕРА
3 Эшли капуста 06-07-21 12:35:27.049000000 ВЕЧЕРА
4 Эшли капуста 06-07-21 12:35:27.049100000 ВЕЧЕРА
5 Адам вода 06-07-21 12:36:27.050000000 ВЕЧЕРА
5 Адам вода 06-07-21 12:36:27.050100000 ВЕЧЕРА

Теперь я хочу удалить самые последние дубликаты записей. Как создать запрос на удаление записей, для которых отметка времени является последней (Name, Item) ?

Ответ №1:

При условии , что столбец NAME ITEM и "DATE" (вам лучше не использовать зарезервированные слова) являются уникальными ключами, вы можете использовать следующий запрос

 DELETE tab
 WHERE (NAME, ITEM, "DATE") IN
       (
        WITH del AS 
        (
         SELECT ID,
                NAME,
                ITEM,
                "DATE",
                ROW_NUMBER() OVER ( PARTITION BY NAME, ITEM ORDER BY "DATE") AS rn
           FROM tab
         )  
       SELECT NAME,ITEM,"DATE" 
         FROM del 
        WHERE rn > 1
       );
 

Это гарантирует , что для каждой комбинации NAME ITEM сохранится только строка с первой «ДАТОЙ» (rn = 1), а все остальные будут удалены.

Этот запрос работает, даже если есть ключи с тройками или строками без дублирования.

Уникальный ключ включен NAME ITEM и "DATE" важен только для того , чтобы сделать запрос детерминированным.

Ответ №2:

Вы можете использовать:

 delete from orders o
    where o.date > (select min(o2.date)
                    from orders o2
                    where o2.name = o.name and o2.item = o.item
                   );
 

Или , если вы хотите использовать not exists , вы можете удалить строки, для которых есть более ранняя строка:

 delete from orders o
    where exists (select 1
                  from orders o2
                  where o2.name = o.name and
                        o2.item = o.item and
                        o2.date < o.date
                 );
 

Только в «первой» строке нет более ранней строки.