PLSQL。如何创建一个RUN表,并确保过程每天只运行一次。

问题描述 投票:0回答:1
  1. 我如何确保我的程序每天只运行一次? 如果它正在运行,任何其他尝试都会被取消。我如何做到这一点?
  2. 此外,我如何创建一个RUN表来跟踪存储过程的运行时间?

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;
plsql plsqldeveloper
1个回答
1
投票

你的过程有一些严重的问题,所以让我们来讨论一下。 从顶层的过程名开始。这里的目的不是为了 "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)运行,满足了你的条件,但仍然有相同的日历运行日期。 建议。

  1. 重新命名你的程序以更清楚地定义其目的。
  2. 放弃在PK前加上日期的想法,只使用这些序列。
  3. 为最后运行日期添加一列,类型为date,但截断为justdate。我为关联的时间同时留下开始日期和结束日期。
  4. 为新的运行日期和模块名称添加一个唯一的键。这有两个作用。它保证了模块在一个日历日只运行一次,并且允许使用同一个日志表在其他模块上执行sameonce-per-day规则。
  5. 改变你的存储过程流程,为这个存储过程插入一条记录,在需要的时候处理dup_row_on_index异常。执行所需的存储过程,然后在完成后设置更新为完成。 有两个问题你还需要考虑。
  6. 当两个或更多的用户试图同时运行存储过程时,会发生什么?
  7. 当My_Procedure本身出现异常时怎么办。
 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; 
© www.soinside.com 2019 - 2024. All rights reserved.