如何在没有"丢失更新"?
的情况下正确归档数据这是我想要做的:
INSERT INTO
SELECT FOR UPDATE
DELETE SELETED rows.
但FOR UPDATE
INSERT INTO ... SELECT...
可以在PL / SQL上使用没有Cursor的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;
答案 0 :(得分:2)
在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;
现在,您可以随时删除这些行。
参考In-Database Archiving in Oracle Database 12c Release 1 (12.1)
答案 1 :(得分:2)
for update
条款对您没有帮助。这只会锁定您查询的行,阻止其他人更新/删除它们。添加的任何新行都没有锁定!因此删除将始终处理新行。
为了克服这个问题,你可以采取几种基本方法:
您可以使用闪回查询执行第一个操作。使用dbms_flashback.get_system_change_number获取数据库的SCN。然后使用“as 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;
/
答案 2 :(得分:1)
您应该可以执行以下操作:
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使用临时表,然后你不必每次都丢弃它。
编辑:使用您添加的新信息,您可以跳过选择更新。只需跟踪您为以下删除复制的ID。使用ids创建一个(可能是临时的)表,执行插入操作,执行删除操作。