假设有两个表TST_SAMPLE (10000 rows)
和TST_SAMPLE_STATUS (empty)
。
我想遍历每条记录TST_SAMPLE
并相应地添加一条记录TST_SAMPLE_STATUS
。
在一个简单的线程中:
begin
for r in (select * from TST_SAMPLE)
loop
insert into TST_SAMPLE_STATUS(rec_id, rec_status)
values (r.rec_id, 'TOUCHED');
end loop;
commit;
end;
/
在多线程解决方案中有一种情况,我不清楚。那么你能解释一下是什么原因导致处理一行TST_SAMPLE
多次。
请参阅下面的详细信息。
create table TST_SAMPLE(
rec_id number(10) primary key
);
create table TST_SAMPLE_STATUS(
rec_id number(10),
rec_status varchar2(10),
session_id varchar2(100)
);
begin
insert into TST_SAMPLE(rec_id)
select LEVEL from dual connect by LEVEL <= 10000;
commit;
end;
/
CREATE OR REPLACE PROCEDURE tst_touch_recs(pi_limit int) is
v_last_iter_count int;
begin
loop
v_last_iter_count := 0;
--------------------------
for r in (select *
from TST_SAMPLE A
where rownum < pi_limit
and NOT EXISTS (select null
from TST_SAMPLE_STATUS B
where B.rec_id = A.rec_id)
FOR UPDATE SKIP LOCKED)
loop
insert into TST_SAMPLE_STATUS(rec_id, rec_status, session_id)
values (r.rec_id, 'TOUCHED', SYS_CONTEXT('USERENV', 'SID'));
v_last_iter_count := v_last_iter_count + 1;
end loop;
commit;
--------------------------
exit when v_last_iter_count = 0;
end loop;
end;
/
在FOR-LOOP
我尝试迭代以下行: - 没有状态(NOT EXISTS 子句) - 当前未锁定在另一个线程中(FOR UPDATE SKIP LOCKED)
对于迭代中的确切行数没有要求。这里pi_limit
只是一个批次的最大尺寸。唯一需要的是TST_SAMPLE
在一个会话中处理每一行。
所以让我们在 3 个线程中运行这个过程。
declare
v_job_id number;
begin
dbms_job.submit(v_job_id, 'begin tst_touch_recs(100); end;', sysdate);
dbms_job.submit(v_job_id, 'begin tst_touch_recs(100); end;', sysdate);
dbms_job.submit(v_job_id, 'begin tst_touch_recs(100); end;', sysdate);
commit;
end;
出乎意料的是,我们看到在几个会话中处理了一些行
select count(unique rec_id) AS unique_count,
count(rec_id) AS total_count
from TST_SAMPLE_STATUS;
| unique_count | total_count |
------------------------------
| 10000 | 17397 |
------------------------------
-- run to see duplicates
select *
from TST_SAMPLE_STATUS
where REC_ID in (
select REC_ID
from TST_SAMPLE_STATUS
group by REC_ID
having count(*) > 1
)
order by REC_ID;
请帮助识别程序实施中的错误tst_touch_recs
。