#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
);
Только в «первой» строке нет более ранней строки.