应用场景
在SQL脚本执行时,很多时候需要等待表的更新或者等待某个条件的达成,才让它继续跑下去,否则跑出来的数是不完整的或者是错误的。可以通过ORACLE的内置软件包 DBMS_LOCK 中的 SLEEP() 存储过程来实现这样一个等表存储过程 WAIT_TABLE ;
存储过程实现
CREATE OR REPLACE PROCEDURE wait_table(
tname VARCHAR2 := NULL, --sql语句或表名,若为表名,属主不能为空
check_freq NUMBER := 300, --默认每5分钟检测一次
check_time NUMBER :=18000, --做多检测多少秒,默认5小时
owner_1 VARCHAR2 := NULL, --表属主
wait_result OUT NUMBER --输出参数,0为失败,1为等表成功
)
IS
p_tname VARCHAR2(32767) := tname;
check_begin NUMBER := 0;
v_sql VARCHAR2(32767);
TYPE cursor_type IS REF CURSOR;
v_cursor cursor_type;
v_id NUMBER;
BEGIN
--初始化等表输出参数
wait_result := 0;
--如果是直接等表名,表名和属主不能为空
IF p_tname IS NOT NULL AND owner_1 IS NOT NULL THEN
p_tname := 'SELECT 1 FROM "' || upper(owner_1) || '"."' || upper(p_tname) || '"';
END IF;
dbms_output.put_line('----开始检测(' || p_tname || ')----');
LOOP
dbms_output.put_line('当前检测时间为:' || to_char(SYSDATE,'yyyy-mm-dd hh24:mi:ss'));
--动态sql
v_sql := 'SELECT 1 FROM (' || p_tname || ') WHERE ROWNUM = 1';
BEGIN
--打开动态游标并关联上动态sql
OPEN v_cursor FOR v_sql;
--取得游标的值放入变量v_id
FETCH v_cursor INTO v_id;
--判断v_id是否为空
IF v_id IS NOT NULL THEN
--等表标识置为1
wait_result := 1;
ELSE
--当前检测没有检测通过,则初始时间后移
check_begin := check_begin + check_freq;
END IF;
--关闭游标
CLOSE v_cursor;
--若动态sql因为某表的不存在而产生异常,则不退出,继续下一次等表
EXCEPTION WHEN OTHERS THEN
check_begin := check_begin + check_freq;
END;
--退出循环的条件就是:等表超时或者等表标识为1
EXIT WHEN check_begin > check_time OR wait_result = 1;
dbms_lock.sleep(check_freq);
END LOOP;
IF wait_result = 1 THEN
dbms_output.put_line('----等表成功----');
ELSE
dbms_output.put_line('----等表超时----');
END IF;
EXCEPTION
WHEN OTHERS THEN
BEGIN
dbms_output.put_line('等表未执行,异常退出!');
dbms_output.put_line('错误信息:' || SQLERRM);
END;
END;
/
注:若没有 DBMS_LOCK 包的使用权限,请用 sys 以 SYSDBA 登录进行授权,执行以下授权语句(这里以用户 apl 为例);
GRANT EXECUTE,DEBUG ON dbms_lock TO apl;
测试
- 等待 tjbb39 下的tmp 表,等待 60 s,每隔 10 s 检测一次,实际上不存在;
DECLARE
res NUMBER;
BEGIN
wait_table('tmp',10,60,'tjbb39',res);
dbms_output.put_line(res);
END;
/
- 在等表的 60 s 期间,创建一个 tmp 表;
CREATE TABLE tmp AS
SELECT 1 id FROM dual;
- 已经存在上面的 tmp 表后,若想等 tmp 表中出现 id = 10 的 记录,等待 60 s ,每隔 10 s 检测一次;
DECLARE
res NUMBER;
BEGIN
wait_table('SELECT * FROM tjbb39.tmp WHERE id = 10',10,60,wait_result => res);
dbms_output.put_line(res);
END;
/
-
表中目前不存在 id 为 10 的记录(或者直接不存在 tmp 表);
-
等表期间向 tmp 表中插入 id 为 10 的记录;
INSERT INTO tjbb39.tmp(id) VALUES(10);
COMMIT;