Правильное архивирование данных без «потерянного обновления» - PullRequest
0 голосов
/ 21 мая 2018

Как правильно архивировать данные без «потерянного обновления»?

Вот что я хочу сделать:

INSERT INTO
SELECT FOR UPDATE

DELETE SELETED rows.

Но синтаксис FOR UPDATE не поддерживается в INSERT INTO ... SELECT...

Можно ли решить проблему, используя только SQL без курсора на PL / SQL?


Пример

create table authority(id number, key varchar2(128));
create table authority_arch(id number, key varchar2(128));

insert into authority(1, 'random_key1');
insert into authority(1, 'random_key2');
insert into authority(1, 'random_key3');
insert into authority(2, 'random_key4');
insert into authority(2, 'random_key5');

commit;

1 сессия

insert into authority_arch
select * from authority where id=2;

-- in this moment 2 session make insert! 'Lose rows' in next delete

delete from authority where id=2;

2 сеанса

insert into authority(2, 'random_key6', sysdate+1);
commit;

в результате есть:

select * from authority

id  |  key
-----------
1   |   random_key1
1   |   random_key2
1   |   random_key3

но я хочу удалить ТОЛЬКО выбранные строки

id  |  key
-----------
1   |   random_key1
1   |   random_key2
1   |   random_key3
2   |   random_key6

В качестве решения я использую:

for rec in (select rowid as rid, a.* from authority a where id=2 FOR UPDATE nowait) loop
    insert into authority_arch values(rec.id, rec.key);
    delete from authority where rowid=rec.rid;
end loop;

Ответы [ 3 ]

0 голосов
/ 21 мая 2018

В Oracle 12c и выше вы можете достичь этого, используя In-Database Archiving.Включение архивирования в базе данных для таблицы приводит к добавлению сгенерированного системой скрытого столбца с именем ORA_ARCHIVE_STATE

. Он использует концепцию «отметка для удаления», поэтому данные остаются в таблице., но не отображается приложению.

Во-первых, включите архивирование в базе данных для своей таблицы.

ALTER TABLE yourtable ROW ARCHIVAL;

Итак, для скрытого столбца с именем ORA_ARCHIVE_STATE создантаблицу, которую вы можете проверить, используя user_tab_cols

Теперь, сделайте ваши строки невидимыми для других приложений / сеансов,

UPDATE yourtable
SET    ORA_ARCHIVE_STATE = '1'
WHERE  id  BETWEEN 1 AND 10000;
COMMIT;

Теперь, вы можете удалить эти строки, когда захотите, используя условие.

См. Архивирование в базе данных в Oracle Database 12c Release 1 (12.1)

0 голосов
/ 21 мая 2018

Предложение for update здесь вам не поможет.Это блокирует только те строки, которые вы запрашиваете, и останавливает других, обновляя / удаляя их.Любые новые добавленные строки не блокируются!Таким образом, удаление всегда будет обрабатывать новые строки.

Чтобы преодолеть это, вы можете воспользоваться несколькими основными подходами:

  • Вставить и удалить строки, как они существовали в определенный момент времени
  • Сохранитьсписок всех строк, которые вы хотите заархивировать.Затем вставьте + удалите, используя этот список, а не саму таблицу.

Первое можно выполнить с помощью запроса на обратную передачу.Получите SCN базы данных с помощью dbms_flashback.get_system_change_number.Затем используйте «as of scn», чтобы получить строки, которые существовали в данный момент:

declare
  insert_time pls_integer;
begin
  insert_time := dbms_flashback.get_system_change_number;
  insert into authority_arch
    select * from authority as of scn insert_time
    where  id = 2;

  dbms_lock.sleep(10); -- wait to allow insert in session 2

  delete authority
  where  ( id, key ) in (
    select id, key from authority as of scn insert_time
    where  id = 2 
  );
end;
/

Вы можете использовать временную таблицу для второго метода.Или вы можете использовать массовую коллекцию для извлечения их в массив.И чтобы вставить + удалить:

declare
  type auth_rec is table of authority%rowtype index by binary_integer;
  arch_recs auth_rec ;
begin

  select * 
  bulk collect into arch_recs
  from   authority
  where  id = 2;

  forall i in arch_recs.first .. arch_recs.last
    insert into authority_arch values arch_recs(i);

  dbms_lock.sleep(10); -- wait to allow insert in session 2

  forall i in arch_recs.first .. arch_recs.last
    delete authority
    where  id = arch_recs(i).id
    and    key = arch_recs(i).key;

end;
/
0 голосов
/ 21 мая 2018

Вы должны быть в состоянии сделать что-то вроде:

create table temp_keys as (select pk from yourtable where condition);
select pk from yourtable where pk in (select pk from temp_keys) for update;
insert into archivetable (columnlist)
    select columnlist from yourtable
        where pk in (select pk from temp_keys);
commit;
drop table temp_keys;

Вы также можете использовать временную таблицу для temp_keys, тогда вам не придется каждый раз удалять ее.

РЕДАКТИРОВАТЬ: С добавленной вами новой информацией вы можете пропустить выбор для обновления.Просто следите за идентификаторами, которые вы скопировали для следующего удаления.Создайте (возможно временную) таблицу с идентификаторами, вставьте, удалите, и все готово.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...