Почему max (rowid) или min (rowid) присутствует при удалении повторяющихся записей? - PullRequest
0 голосов
/ 27 апреля 2018

Можем ли мы удалить дубликаты записей без использования псевдо-столбца rowid ... что означает значение max (rowid) / min (rowid) при удалении дублирующихся записей?

Ответы [ 2 ]

0 голосов
/ 27 апреля 2018

Если вся строка дублируется и вы хотите удалить все копии, кроме одной, в SQL нет простого способа выбрать строки, которые вы хотите удалить, не используя системный адрес строки.

Используя таблицу 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;

это не очень хорошее решение, так как мы должны знать идентификаторы и количество и применять их к одному идентификатору за раз.

Мы можем удалить их без явного указания 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 );

Находит все «предыдущие» строки в порядке даты и удаляет соответствующие строки. Последняя строка в каждом наборе не появится в этом списке и не будет удалена.

0 голосов
/ 27 апреля 2018

ROWID - внутренний идентификатор строки, используемый Oracle для поиска физической записи. Поэтому, даже если у вас могут быть повторные значения для вашего «идентификатора», каждый 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
       );
...