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

hmae6n7t  于 2021-06-23  发布在  Mysql
关注(0)|答案(2)|浏览(899)

我们能不使用伪冒号删除重复的记录吗 rowid ... 删除双面打印记录时,中的max(rowid)/min(rowid)是什么意思?

6vl6ewon

6vl6ewon1#

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方法消除我的示例表的重复,您将错误地删除最新的price 9(如upd\u 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
       );
bkkx9g8r

bkkx9g8r2#

如果整行是重复的,并且您希望删除除一个副本以外的所有副本,那么在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,并删除相应的行。每组中的最后一行不会出现在该列表中,因此不会被删除。

相关问题