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
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
);
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 );
2条答案
按热度按时间6vl6ewon1#
ROWID
oracle用于定位物理记录的内部行标识符。因此,即使您的“id”有重复的值,每个记录rowid仍然是唯一的。组中的max(rowid)通常是最近插入的记录,但是这种假设在生产代码中常常是错误的。只能依靠它来删除一个完美的副本。完美的复制品就是
select distinct *
结果是一个记录。对于所有其他用途,你需要一个鉴别器。鉴别器列可用于区分两个记录,例如,更新日期指示修改时间。如果您使用典型的rowid方法消除我的示例表的重复,您将错误地删除最新的price 9(如upd\u date所示)。
更好的方法是首先使用鉴别器,然后作为最后手段使用rowid。
bkkx9g8r2#
如果整行是重复的,并且您希望删除除一个副本以外的所有副本,那么在sql中,如果不使用系统生成的行地址,就很难选择要删除的行。
使用ronnis的
PRICES
表作为一个例子,我们看到有三行B
完全相同:尽管我们可能会用
这不是一个好的解决方案,因为我们必须知道id和计数,并一次应用于一个id。
我们可以在不使用pl/sql显式指定rowid的情况下删除它们:
当然,内部
where current of
语法正在使用rowid。显式使用rowid使这变得更简单:
这将按日期顺序查找所有“上一个”rowid,并删除相应的行。每组中的最后一行不会出现在该列表中,因此不会被删除。