Для вашей версии запроса:
With CTE_Duplicates as (
select t.*,
row_number() over (partition by transaction_id order by last_modified_date desc) as rownumber
from TRANSACTIONS
)
delete from CTE_Duplicates
where rownumber > 1;
Требуется индекс для (transaction_id, last_modified_date desc)
. Однако с тем же индексом может быть быстрее сформулировать запрос следующим образом:
delete t from transactions t
where t.last_modified_date = (select max(t2.last_modified_date)
from transactions t2
where t2.transaction_id = t.transaction_id
);
Все это говорит о том, что ваш запрос будет довольно дорогим, если будет удалено много строк («многие» могут даже быть несколько процентов). В этом случае решение для временной таблицы может быть лучше:
select t.*
into temp_transactions
from transactions t
where t.last_modified_date = (select max(t2.last_modified_date)
from transactions t2
where t2.transaction_id = t.transaction_id
);
truncation table temp_transactions; -- backup first!
insert into transactions
select *
from temp_transactions;
Конечно, логика c будет более сложной, если у вас есть идентификаторы столбцов или триггеры, которые устанавливают значения в таблице.