create table run_log
(runID NUMBER PRIMARY KEY,
ModuleName VARCHAR2(35) NOT NULL,
RunStartDate DATE NOT NULL,
RunEndDate DATE,
Outcome VARCHAR2(25),
Comments VARCHAR2(255));
create sequence seq_runID
minvalue 1
start with 1
maxvalue 9999999
increment by 1;
create or replace procedure run_table IS
v_runlogrecord run_log%ROWTYPE;
v_runlogID NUMBER;
v_unique VARCHAR(20) := to_char(sysdate, 'DDMMYYYY') || '000';
moduleRan EXCEPTION;
c_buffer CONSTANT NUMBER :=23/24;
c_moduleName VARCHAR(25) :='RUN_TABLE';
Begin
begin
My_procedure;
end;
begin
select * INTO v_runlogrecord
from run_log
where UPPER(moduleName) = c_moduleName
AND outcome = 'SUCCESS'
and RunEndDate > (sysdate-c_buffer);
RAISE moduleRan;
Exception
when NO_DATA_FOUND then
SELECT seq_runID.NEXTVAL INTO v_runLogID from dual;
v_runlogID := v_unique + v_runlogID;
INSERT INTO run_log(runID, ModuleName, RunStartDate, RunEndDate, Outcome, Comments)
VALUES(v_runlogID, c_moduleName, sysdate, NULL, NULL, 'Start Program');
end;
UPDATE run_log
set runenddate = sysdate,
outcome = 'SUCCESS',
comments = 'Run Completed'
where runid = v_runlogID;
EXCEPTION
WHEN moduleRan THEN
DBMS_OUTPUT.PUT_LINE('Already run!');
END;
set SERVEROUTPUT on
exec run_table;
你的过程有一些严重的问题,所以让我们来讨论一下。 从顶层的过程名开始。这里的目的不是为了 "Run_Table",而是为了运行 "My_Procedure",同时执行Business_Rule,即每天只运行一次。 接下来你的尝试是生成一个主键。你创建了一个序列,这本身就足够了,但你仔细构造了一个密钥格式,从日期 作为字符串. 只需将seq.nextval添加到你的字符串中作为一个键。但是seq.nextval达到1000会怎么样呢?好吧,你刚刚毁掉了精心构造的日期定义键。 执行顺序是相当曲折的。首先我们要做的是运行过程,即使它已经被运行了。然后检查它是否已经运行。如果是这样,你会提出用户定义的错误,并在一个异常块中捕获。不幸的是,该块只是发出了一个dbms_output消息(可能在生产环境中不可用).但它本身并没有引发一个异常,也没有发出回滚。因此,你的过程已经成功执行了第二次。 最后,你的运行间隔时间(2324)并没有完全执行一天一次,而是每23小时一次。因此,它在00:05 (12:05 AM)和23:10 (11:10 PM)运行,满足了你的条件,但仍然有相同的日历运行日期。 建议。
create table run_log
( runid integer
, last_run_date date not null
, modulename varchar2(35) not null
, runstartdate date not null
, runenddate date
, outcome varchar2(25)
, comments varchar2(255)
, constraint run_log_pk
primary key (runid)
, constraint run_log_bk
unique (last_run_date, modulename)
, constraint last_run_date_no_time_ck
check ( trunc(last_run_date) = last_run_date) )
);
create sequence seq_runid
minvalue 1
start with 1
maxvalue 9999999
increment by 1;
create or replace procedure my_procedure_daily_run is
c_modulename constant varchar(25) :='my_procedure';
c_run_date constant date := trunc(sysdate);
begin
-- enforce Business Rule: Run Once per Day
insert into run_log(runid, last_run_date, modulename, runstartdate, runenddate, outcome, comments)
values( seq_runid.nextval, c_run_date, c_modulename, sysdate, null, null, 'Start Program');
-- run procedure
my_procedure;
-- tag today's run message as complete.
update run_log
set runenddate = sysdate
, outcome = 'SUCCESS'
, comments = 'Run Completed'
where last_run_date = c_run_date
and modulename = c_modulename;
exception
when dup_val_on_index then
raise_application_error( -20001, c_modulename || ' has already run for ' || to_char(c_run_date, 'yyyy-mm-dd'));
end my_procedure_daily_run;
--- Test ok
begin
my_procedure_daily_run;
end;
--- Test second run
begin
my_procedure_daily_run;
end;