删除重复记录时,为什么max(rowid)或min(rowid)存在?

时间:2018-04-27 04:16:50

标签: mysql oracle rowid

我们可以在不使用Pseudo coloumn rowid的情况下删除重复记录吗... 删除重复记录时,max(rowid)/ min(rowid)的含义是什么?

2 个答案:

答案 0 :(得分:0)

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
       );

答案 1 :(得分:0)

如果整个行都是重复的,并且您希望删除除一个副本以外的所有行,则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和计数,并一次应用于一个ID。

我们可以删除它们而无需使用PL / SQL显式指定rowid:

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,并删除相应的行。每个集合中的最后一行不会出现在该列表中,因此不会被删除。