解決客戶端呼叫長時間儲存程序無回應、無進度回報的問題:
-- 1. 啟用 Job Queue(需重啟實例)
-- 9i/10g: JOB_QUEUE_PROCESSES = 10
-- 8i: 另需 JOB_QUEUE_INTERVAL = 1
-- 2. 授權必要權限
GRANT EXECUTE ON DBMS_PIPE TO app_user;
GRANT EXECUTE ON DBMS_LOCK TO app_user;
GRANT EXECUTE ON DBMS_JOB TO app_user;
CREATE OR REPLACE PROCEDURE Long_Running_Task IS
v_status NUMBER;
BEGIN
FOR i IN 1..30 LOOP
DBMS_PIPE.PURGE('task_pipe');
DBMS_PIPE.PACK_MESSAGE(i);
v_status := DBMS_PIPE.SEND_MESSAGE('task_pipe');
DBMS_LOCK.SLEEP(1); -- 模擬工作
END LOOP;
-- 完成標記
DBMS_PIPE.PURGE('task_pipe');
DBMS_PIPE.PACK_MESSAGE(999999);
v_status := DBMS_PIPE.SEND_MESSAGE('task_pipe');
END;
/
CREATE OR REPLACE PROCEDURE Get_Task_Progress(p_progress OUT NUMBER) IS
v_status NUMBER;
v_msg NUMBER;
BEGIN
-- 等待 0 秒:有訊息即讀,無則立即返回
v_status := DBMS_PIPE.RECEIVE_MESSAGE('task_pipe', 0);
IF v_status != 0 THEN
p_progress := -1; -- 無訊息或逾時
RETURN;
END IF;
DBMS_PIPE.UNPACK_MESSAGE(v_msg);
p_progress := v_msg;
END;
/
CREATE OR REPLACE PROCEDURE Start_Task(p_job_no OUT NUMBER) IS
BEGIN
DBMS_JOB.SUBMIT(p_job_no, 'Long_Running_Task;', SYSDATE, NULL, FALSE);
COMMIT; -- 必須 Commit Job 才會被 Job Queue Process 拾取
END;
/
-- 1. 啟動任務
VAR job_no NUMBER;
EXEC Start_Task(:job_no);
PRINT job_no;
-- 2. 輪詢進度
VAR progress NUMBER;
BEGIN
LOOP
EXEC Get_Task_Progress(:progress);
EXIT WHEN :progress IN (-1, 999999);
DBMS_OUTPUT.PUT_LINE('Progress: ' || :progress);
DBMS_LOCK.SLEEP(2);
END LOOP;
IF :progress = 999999 THEN
DBMS_OUTPUT.PUT_LINE('Task Completed!');
END IF;
END;
/
RECEIVE_MESSAGE(pipe, 0) 參數 0 代表非阻塞,關鍵避免監控程序卡住。COMMIT,否則不會被執行。'task_pipe_' || DBMS_SESSION.UNIQUE_SESSION_ID。DBMS_SCHEDULER(10g+)功能更強大。