Можно ли удалить дубликаты записей без использования Псевдо coloumn rowid
... что значение макс (RowId)/мин (ROWID) в то время как удаление duplicte записи?
Если вся строка дублируется и вы хотите удалить все, кроме одной копии, в SQL нет простого способа выбрать строки, которые вы хотите удалить, не используя системный адрес строки.
Использование Ronnis' PRICES
таблицы в качестве примера, мы видим, что есть три строки для B
, которые являются точными копиями:
ID PRICE UPD_DATE
-- ----- -----------
A 7 10/04/2018
B 8 09/04/2018
B 8 09/04/2018
B 8 09/04/2018
C 7 04/04/2018
C 8 05/04/2018
C 9 06/04/2018
Хотя мы могли бы использовать что-то вроде
delete prices where id = 'B' and rownum <= 2;
это нехорошее решение, так как мы должны знать идентификаторы и подсчеты и применять к одному ID за раз.
Мы можем удалить их без явного указания rowid с помощью PL/SQL:
declare
cursor c_prices is
select id, price
, row_number() over (partition by id order by upd_date desc) as seq
from prices
for update;
begin
for r in c_prices
loop
if r.seq > 1 then
delete prices where current of c_prices;
end if;
end loop;
end;
хотя, конечно, внутри, where current of
синтаксиса используется rowid.
Использование rowid явно делает это намного проще:
delete prices where rowid in
( select lag(rowid) over (partition by id order by upd_date) from prices );
Это находит все "предыдущие" ряды по порядку даты и удаляет соответствующие строки. Последняя строка в каждом наборе не будет отображаться в этом списке и поэтому не будет удалена.
ROWID
- это внутренний идентификатор строки, используемый Oracle для поиска физической записи. Поэтому, даже если у вас могут быть повторяющиеся значения для вашего "ID", каждая запись ROWID будет по-прежнему уникальной.
create table prices(
id varchar2(15) not null
,price number not null
,upd_date date not null
-- ,primary key(id)
);
ROWID ID PRICE UPD_DATE
------------------ -- ----- ----------
AChTgbADaAAFgxYAAA A 7 2018-04-10
AChTgbADaAAFgxYAAB B 8 2018-04-09
AChTgbADaAAFgxYAAC B 8 2018-04-09
AChTgbADaAAFgxYAAD B 8 2018-04-09
AChTgbADaAAFgxYAAE C 9 2018-04-06
AChTgbADaAAFgxYAAF C 8 2018-04-05
AChTgbADaAAFgxYAAG C 7 2018-04-04
MAX (rowid) в группе часто является самой последней вставленной записью, но это предположение неверно слишком часто, чтобы опираться на производственный код. На него можно полагаться только на удаление идеального дубликата. Идеальный дубликат - это тот, в котором select distinct *
результат в одной записи. Для всех других целей вам нужен дискриминатор. Столбец дискриминатора может использоваться, чтобы разделить две записи, например, с датой обновления, которая указывает время изменения.
Если вы удалите дубликат моей таблицы с типичным подходом ROWID, вы бы неправильно удалили самую последнюю цену 9 (о чем свидетельствует upd_date).
delete
from prices
where rowid not in(
select max(rowid)
from prices
group by id);
Лучшим подходом было бы использовать дискриминатор сначала, а затем в качестве последнего средства использовать ROWID.
delete
from prices
where rowid in(
select rid
from (select rowid as rid
,row_number() over( -- Assign a sequence number
partition by id -- Group rows by ID
order by upd_date desc -- Sort them by upd_date first
,rowid desc -- Then by ROWID
) as rn
from prices
)
-- The most recent record will be rn = 1.
-- The second most recent record will be rn = 2, etcetera
where rn > 1 -- Select only the duplicates ("after" the most recent one record
);
ROWID
является наиболее эффективным.rowid
полезен.