Oracle 仅当所有并发事务都成功时才提交它们

Oracle 仅当所有并发事务都成功时才提交它们,oracle,plsql,oracle11g,parallel-processing,Oracle,Plsql,Oracle11g,Parallel Processing,我想要的是并发执行一个过程(将在Oracle11g上运行),并在所有并发事务成功时提交整个操作 我想到的两种并行执行方式是DBMS\u parallel\u EXECUTE和DBMS\u job.submit(),但据我所知,在这两种情况下,创建的进程都在各自的会话中运行,每个进程在终止时提交更改(或者在发生错误的情况下,可以回滚自己的更改) 我想要的是,启动并行进程,等待每一个进程完成,检查它们是否都成功,然后提交更改(或者在至少一个进程失败时回滚) 上述情况是否可能?(如何实施) 谢谢。我很

我想要的是并发执行一个过程(将在Oracle11g上运行),并在所有并发事务成功时提交整个操作

我想到的两种并行执行方式是
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()的调用
。是的,这应该行得通。问题是,如果其中一个线程死亡,您需要以某种方式监视线程,并手动终止剩余的会话。或者您可以将其自动化,但您已经开始构建大量框架代码。如果这是一项常规任务,那么显然是值得的。