Oracle 仅当所有并发事务都成功时才提交它们
我想要的是并发执行一个过程(将在Oracle11g上运行),并在所有并发事务成功时提交整个操作 我想到的两种并行执行方式是Oracle 仅当所有并发事务都成功时才提交它们,oracle,plsql,oracle11g,parallel-processing,Oracle,Plsql,Oracle11g,Parallel Processing,我想要的是并发执行一个过程(将在Oracle11g上运行),并在所有并发事务成功时提交整个操作 我想到的两种并行执行方式是DBMS\u parallel\u EXECUTE和DBMS\u job.submit(),但据我所知,在这两种情况下,创建的进程都在各自的会话中运行,每个进程在终止时提交更改(或者在发生错误的情况下,可以回滚自己的更改) 我想要的是,启动并行进程,等待每一个进程完成,检查它们是否都成功,然后提交更改(或者在至少一个进程失败时回滚) 上述情况是否可能?(如何实施) 谢谢。我很
DBMS\u parallel\u EXECUTE
和DBMS\u job.submit()
,但据我所知,在这两种情况下,创建的进程都在各自的会话中运行,每个进程在终止时提交更改(或者在发生错误的情况下,可以回滚自己的更改)
我想要的是,启动并行进程,等待每一个进程完成,检查它们是否都成功,然后提交更改(或者在至少一个进程失败时回滚)
上述情况是否可能?(如何实施)
谢谢。我很好奇为什么会有这样的要求;我可能会问,如果我有这个要求,是否真的有必要。但是如果你不能质疑这个要求,我会这样做
-- We need to have a common persistent location for all of the jobs to read
CREATE TABLE test_tab
(
job_name VARCHAR2(30),
status VARCHAR2(30)
)
/
-- The procedure writing to our table must be autonomous so that updates occur
-- without committing the rest of the work
CREATE OR REPLACE PROCEDURE test_log
(
i_job_name IN VARCHAR2,
i_status IN VARCHAR2
) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
MERGE INTO test_tab tgt
USING dual
ON (tgt.job_name = i_job_name)
WHEN MATCHED THEN
UPDATE SET status = i_status
WHEN NOT MATCHED THEN
INSERT VALUES (i_job_name, i_status);
COMMIT;
END test_log;
/
CREATE OR REPLACE PROCEDURE test_proc(i_job_name IN VARCHAR2) IS
l_complete_cnt INTEGER;
l_error_cnt INTEGER;
l_waiting BOOLEAN := TRUE;
BEGIN
-- !!! Your code here !!!
/* -- Uncomment this block to prove the rollback scenario.
IF i_job_name LIKE '%8' THEN
raise_application_error(-20001, 'Throwing an error to prove rollback.');
END IF;*/
test_log(i_job_name, 'COMPLETE');
WHILE l_waiting LOOP
SELECT SUM(CASE WHEN status IN ('COMPLETE', 'COMMITTED') THEN 1 ELSE 0 END)
,SUM(CASE WHEN status = 'ERROR' THEN 1 ELSE 0 END)
INTO l_complete_cnt
,l_error_cnt
FROM test_tab
WHERE REGEXP_LIKE(job_name, 'TEST_JOB_\d');
IF l_complete_cnt = 8 THEN
COMMIT;
test_log(i_job_name, 'COMMITTED');
l_waiting := FALSE;
ELSIF l_error_cnt > 0 THEN
ROLLBACK;
test_log(i_job_name, 'ROLLBACK');
l_waiting := FALSE;
ELSE
dbms_lock.sleep(seconds => 5);
END IF;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
test_log(i_job_name, 'ERROR');
RAISE;
END;
/
-- Begin test section
BEGIN
FOR i IN 1..8 LOOP
dbms_scheduler.create_job('TEST_JOB_'||i
,'PLSQL_BLOCK'
,'BEGIN test_proc(''TEST_JOB_'||i||'''); END;'
,start_date => SYSDATE
,enabled => TRUE);
END LOOP;
END;
/
SELECT * FROM test_tab;
TRUNCATE TABLE test_tab; --the table should be cleared between tests
我很好奇为什么会有这样的要求;我可能会问,如果我有这个要求,是否真的有必要。但如果你不能质疑这个要求,我会这样做
-- We need to have a common persistent location for all of the jobs to read
CREATE TABLE test_tab
(
job_name VARCHAR2(30),
status VARCHAR2(30)
)
/
-- The procedure writing to our table must be autonomous so that updates occur
-- without committing the rest of the work
CREATE OR REPLACE PROCEDURE test_log
(
i_job_name IN VARCHAR2,
i_status IN VARCHAR2
) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
MERGE INTO test_tab tgt
USING dual
ON (tgt.job_name = i_job_name)
WHEN MATCHED THEN
UPDATE SET status = i_status
WHEN NOT MATCHED THEN
INSERT VALUES (i_job_name, i_status);
COMMIT;
END test_log;
/
CREATE OR REPLACE PROCEDURE test_proc(i_job_name IN VARCHAR2) IS
l_complete_cnt INTEGER;
l_error_cnt INTEGER;
l_waiting BOOLEAN := TRUE;
BEGIN
-- !!! Your code here !!!
/* -- Uncomment this block to prove the rollback scenario.
IF i_job_name LIKE '%8' THEN
raise_application_error(-20001, 'Throwing an error to prove rollback.');
END IF;*/
test_log(i_job_name, 'COMPLETE');
WHILE l_waiting LOOP
SELECT SUM(CASE WHEN status IN ('COMPLETE', 'COMMITTED') THEN 1 ELSE 0 END)
,SUM(CASE WHEN status = 'ERROR' THEN 1 ELSE 0 END)
INTO l_complete_cnt
,l_error_cnt
FROM test_tab
WHERE REGEXP_LIKE(job_name, 'TEST_JOB_\d');
IF l_complete_cnt = 8 THEN
COMMIT;
test_log(i_job_name, 'COMMITTED');
l_waiting := FALSE;
ELSIF l_error_cnt > 0 THEN
ROLLBACK;
test_log(i_job_name, 'ROLLBACK');
l_waiting := FALSE;
ELSE
dbms_lock.sleep(seconds => 5);
END IF;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
test_log(i_job_name, 'ERROR');
RAISE;
END;
/
-- Begin test section
BEGIN
FOR i IN 1..8 LOOP
dbms_scheduler.create_job('TEST_JOB_'||i
,'PLSQL_BLOCK'
,'BEGIN test_proc(''TEST_JOB_'||i||'''); END;'
,start_date => SYSDATE
,enabled => TRUE);
END LOOP;
END;
/
SELECT * FROM test_tab;
TRUNCATE TABLE test_tab; --the table should be cleared between tests
如果你对“为什么”感到好奇你可以看一下:他们上周左右提出的问题似乎都与此相关。@APC我觉得上面的方法应该有效。但你的评论来了…;-/在我看来-每项成功的工作都会在表格中记录“完成”。如果每个人都成功完成了任务,那么
l_complete\u cnt
就变成了8
并且每个人都提交(仍然l_complete\u cnt
仍然8
),另一方面,如果至少有一个错误,每个人都回滚
-我错过了什么?Doh!错过了对dbms\u lock.sleep()的调用
。是的,这应该可以。问题是,如果其中一个线程死亡,您需要以某种方式监视线程,并手动终止剩余的会话。或者您可以将其自动化,但您已经开始构建大量框架代码。如果这是一项常规任务,显然值得。如果您对“为什么”感到好奇你可以看一下:他们上周左右提出的问题似乎都与此相关。@APC我觉得上面的方法应该有效。但你的评论来了…;-/在我看来-每项成功的工作都会在表格中记录“完成”。如果每个人都成功完成了任务,那么l_complete\u cnt
就变成了8
并且每个人都提交(仍然l_complete\u cnt
仍然8
),另一方面,如果至少有一个错误,每个人都回滚
-我错过了什么?Doh!错过了对dbms\u lock.sleep()的调用
。是的,这应该行得通。问题是,如果其中一个线程死亡,您需要以某种方式监视线程,并手动终止剩余的会话。或者您可以将其自动化,但您已经开始构建大量框架代码。如果这是一项常规任务,那么显然是值得的。