ORACLE存储过程实现等表WAIT_TABLE

应用场景

在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 包的使用权限,请用 sysSYSDBA 登录进行授权,执行以下授权语句(这里以用户 apl 为例);

GRANT EXECUTE,DEBUG ON dbms_lock TO apl;

测试

  1. 等待 tjbb39 下的tmp 表,等待 60 s,每隔 10 s 检测一次,实际上不存在;
DECLARE
  res NUMBER;
BEGIN
  wait_table('tmp',10,60,'tjbb39',res);
  dbms_output.put_line(res);
END;
/

等表超时1

  1. 在等表的 60 s 期间,创建一个 tmp 表;
CREATE TABLE tmp AS
SELECT 1 id FROM dual;

等表成功1

  1. 已经存在上面的 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;
/
  1. 表中目前不存在 id10 的记录(或者直接不存在 tmp 表);
    等表超时2

  2. 等表期间向 tmp 表中插入 id10 的记录;

INSERT INTO tjbb39.tmp(id) VALUES(10);
COMMIT; 

等表成功2

猜你喜欢

转载自blog.csdn.net/qq_33445829/article/details/112463408
今日推荐