一般我們會將一些涉及到數據庫的定時任務直接用存儲過程搞定,省去了后端代碼的開發、部署,簡單、快速,但這種方式存在一個弊端——當存儲過程執行出錯了,我們無法感知。解決辦法也簡單,學代碼那樣去捕獲異常、打印日志。
第一步,建日志表:
create table TBL_WLF_SYS_LOG ( S_TIME VARCHAR2(32) not null, S_LEVEL VARCHAR2(32), S_PROCNAME VARCHAR2(64), S_MSG VARCHAR2(4000), S_ADVICE VARCHAR2(1024) ) tablespace TBS_WLF_DAT; -- Add comments to the table comment on table TBL_WLF_SYS_LOG is '存儲過程日志表'; -- Add comments to the columns comment on column TBL_WLF_SYS_LOG.S_TIME is '操作時間'; -- Add comments to the columns comment on column TBL_WLF_SYS_LOG.S_LEVEL is '操作級別'; -- Add comments to the columns comment on column TBL_WLF_SYS_LOG.S_PROCNAME is '執行存儲過程名稱'; -- Add comments to the columns comment on column TBL_WLF_SYS_LOG.S_MSG is '錯誤信息'; -- Add comments to the columns comment on column TBL_WLF_SYS_LOG.S_ADVICE is '建議信息';
第二步,建日志存儲過程:
CREATE OR REPLACE PROCEDURE VCODE.prc_wlf_sys_writelog( i_flag INTEGER, i_id INTEGER, str_procname varchar2, str_msg varchar2, str_advice varchar2 ) IS -- 操作時間 str_time varchar2(32); -- 操作級別 str_level varchar2(32); -- 執行存儲過程名稱 p_procname varchar2(1024); -- 錯誤信息,或者記錄信息 p_msg varchar2(1024); -- 建議信息 p_advice varchar2(1024); BEGIN IF (i_flag = 2 AND i_id >= 1 AND i_id <= 4) THEN CASE WHEN i_id = 1 THEN str_level := 'log'; WHEN i_id = 2 THEN str_level := 'debug'; WHEN i_id = 3 THEN str_level := 'alarm'; ELSE str_level := 'error'; END CASE; p_procname := str_procname; p_msg := str_msg; p_advice := str_advice; ELSE str_level := 'error'; p_procname := 'p_public_writelog'; p_msg := 'writelog_error'; p_advice := ''; END IF; str_time := to_char(SYSDATE, 'yyyy-mm-dd hh24:mi:ss'); INSERT INTO tbl_wlf_sys_log (s_time, s_level, s_procname, s_msg, s_advice) VALUES (str_time, str_level, p_procname, p_msg, p_advice); COMMIT; END prc_wlf_sys_writelog; /
第三步,在我們業務存儲過程中,調用日志存儲過程:
CREATE OR REPLACE procedure VCODE.PROC_CUSTOM_RECORD_UPDATE is -- debug信息 v_debugmsg varchar2(1024); -- 錯誤信息 v_errmsg varchar2(1024); -- 查詢用戶邀請活動信息表 獲取活動開始與結束時間 cursor ACTIVITY_CUR is --聲明顯式游標 select T.ACTIVITYID, T.COUNTSTARTTIME, T.COUNTENDTIME from vcode.T_INVITE_ACTIVITYINFO T where T.HASCOUNTTIME = 1; --定義游標變量,該變量的類型為基於游標ACTIVITY_CUR的記錄 type ACTIVITY_CUR_ROW is table of ACTIVITY_CUR%ROWTYPE; cs_invitestat SYS_REFCURSOR; type tp_CUSTOM_RECORD is table of T_INVITER_CUSTOM_RECORD%ROWTYPE; va_CUSTOM_RECORD tp_CUSTOM_RECORD; ACTIVITY_ID varchar2(50); START_TIME date; END_TIME date; begin -- 存儲過程開始日志 v_debugmsg := 'VCODE.PROC_CUSTOM_RECORD_UPDATE begin log- '; prc_wlf_sys_writelog(2, 2, 'PROC_CUSTOM_RECORD_UPDATE', v_debugmsg, ''); --For 循環 遍歷用戶邀請活動信息表,根據活動開始結束時間間隔獲取數據信息 for ACTIVITY_CUR_ROW in ACTIVITY_CUR LOOP ACTIVITY_ID := ACTIVITY_CUR_ROW.ACTIVITYID; START_TIME := ACTIVITY_CUR_ROW.COUNTSTARTTIME; -- 取當天的最后一秒 select TRUNC(ACTIVITY_CUR_ROW.COUNTENDTIME+1)-1/(24*3600) into END_TIME from DUAL; -- 根據活動ID,開始時間,結束時間,查詢被邀請人記錄表獲取邀請人激活人數與最后激活時間,查詢獎勵記錄表獲取書券獎勵,並關聯一起。 open cs_invitestat for SELECT t4.INVITERMSISDN,COUNT(1) AS TOTALACTIVENUMBER,max(t4.ACTIVETIME) AS LASTACTIVETIME,t4.ACTIVITYID,CASE WHEN SUM(t3.PRIZENUM) is null THEN 0 ELSE SUM(t3.PRIZENUM) END totalTicket FROM ( SELECT t1.INVITERMSISDN,t1.INVITEEMSISDN,t1.ACTIVETIME,t1.ACTIVITYID FROM T_INVITEE_RECORD t1 WHERE t1.ACTIVITYID=ACTIVITY_ID and t1.ACTIVESTATUS = 1 and t1.INVITEETYPE = 0 and t1.ACTIVETIME <= END_TIME and t1.ACTIVETIME >= START_TIME ) t4 LEFT JOIN (SELECT t2.INVITERMSISDN,t2.INVITEEMSISDN,SUM(t2.PRIZENUM) PRIZENUM,t2.ACTIVETIME FROM T_INVITING_AWARDS_RECORD t2 WHERE t2.PRIZETYPE=3 AND t2.ISSEND IN (0,1,2,3,10) AND t2.ACTIVITYID=ACTIVITY_ID and t2.ACTIVETIME <= END_TIME and t2.ACTIVETIME >= START_TIME and t2.REWARDTYPE = 0 group by t2.ACTIVETIME,t2.INVITEEMSISDN,t2.INVITERMSISDN) t3 ON t4.INVITERMSISDN=t3.INVITERMSISDN AND t4.INVITEEMSISDN=t3.INVITEEMSISDN AND t4.ACTIVETIME=t3.ACTIVETIME group by t4.ACTIVITYID,t4.INVITERMSISDN ORDER BY TOTALACTIVENUMBER desc,LASTACTIVETIME ASC; fetch cs_invitestat bulk collect into va_CUSTOM_RECORD limit 500; -- 遍歷結果,並插入T_INVITER_CUSTOM_RECORD 自定義排行表中,如果存在數據則更新(邀請人、活動ID相同),不存在則插入 forall i in 1..va_CUSTOM_RECORD.count merge into vcode.T_INVITER_CUSTOM_RECORD T5 using (select * from dual) on (INVITERMSISDN = va_CUSTOM_RECORD(i).INVITERMSISDN AND ACTIVITYID=va_CUSTOM_RECORD(i).ACTIVITYID) when matched then update set TOTALACTIVENUMBER =va_CUSTOM_RECORD(i).TOTALACTIVENUMBER, LASTACTIVETIME =va_CUSTOM_RECORD(i).LASTACTIVETIME, TOTALTICKET =va_CUSTOM_RECORD(i).TOTALTICKET where T5.TOTALACTIVENUMBER!=va_CUSTOM_RECORD(i).TOTALACTIVENUMBER OR T5.TOTALTICKET!=va_CUSTOM_RECORD(i).TOTALTICKET when not matched then insert ( INVITERMSISDN, TOTALACTIVENUMBER, LASTACTIVETIME, ACTIVITYID, TOTALTICKET ) values ( va_CUSTOM_RECORD(i).INVITERMSISDN, va_CUSTOM_RECORD(i).TOTALACTIVENUMBER, va_CUSTOM_RECORD(i).LASTACTIVETIME, va_CUSTOM_RECORD(i).ACTIVITYID, va_CUSTOM_RECORD(i).TOTALTICKET ); commit; end LOOP; -- 存儲過程開始日志 v_debugmsg := 'VCODE.PROC_CUSTOM_RECORD_UPDATE end log- '; prc_wlf_sys_writelog(2, 2, 'PROC_CUSTOM_RECORD_UPDATE', v_debugmsg, ''); exception when others then begin rollback; v_errmsg := 'sqlexception~~sqlcode:' || to_char(sqlcode) || ' sqlstate:' || substr(sqlerrm, 1, 512); prc_wlf_sys_writelog(2, 4, 'PROC_CUSTOM_RECORD_UPDATE', v_errmsg, ''); end; end; /
