Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1702932
  • 博文数量: 136
  • 博客积分: 10021
  • 博客等级: 上将
  • 技术积分: 3261
  • 用 户 组: 普通用户
  • 注册时间: 2007-01-22 11:26
文章分类

全部博文(136)

文章存档

2010年(1)

2009年(26)

2008年(109)

我的朋友

分类: Oracle

2008-03-27 21:02:37

一、JOB时间间隔的问题
我们MIS系统中需要建立一些JOB,比如客户会要求在月底自动生成一些报表统计信息。
 
在指定JOB的执行时间时,一般我们会选择在每月月底的午夜来执行(或者有多个JOB,应该分散在不同的时间段来执行)。此时不能直接用sysdate来确定时间,因为每次执行后,oracle自动生成的下一次的执行时间,会有几秒的延迟。
 
比如,我指定第一次执行时间为:2008-03-27 10:58:01,时间间隔的方式为:last_day(add_months(sysdate, 1))
那么,JOB执行后我会发现,第一次的执行时间是2008-03-27 10:58:05,而下一次的执行时间被更新为2008-04-30 10:58:05;而下次执行后,发现它的执行时间是2008-04-30 10:58:07,而它再次执行的时间变为2008-05-31 10:58:07;……
 
二、可能原因
(1)JOB的调用有时间延迟。我指定在10:58:01执行,可是由于延迟,真正执行的时间是10:58:05
(2)根据实际的执行时间来自动计算下一次的执行时间。我指定下一次的时间计算方法是last_day(add_months(sysdate, 1))。但它是根据10:58:05来计算的,而不是10:58:01
 
三、解决办法
这种延迟累计起来,就可能导致执行时间错误!为避免这种延迟,我们应该手工指定执行时间中的“时分秒”。比如,
每月月底的午夜:
last_day(add_months(to_date(to_char(sysdate, 'yyyy-mm-dd'), 'yyyy-mm-dd'), 1))
每月的任意指定时间:
last_day(add_months(to_date(to_char(sysdate, 'yyyy-mm-dd') || ' 11:24:31', 'yyyy-mm-dd hh24:mi:ss'), 1))
……
这样,每次执行的时间大概会比指定的时间延迟2-5秒,但oracle自动计算出来的下一次执行时间是没有问题的,所以不会累计这个延迟。
 
四、相关测试
下面是一个小测试:
SQL> conn tianyc/test
已连接。
SQL> select * from v$version;
 
BANNER
-----------------------------------------------------------
Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
PL/SQL Release 9.2.0.1.0 - Production
CORE    9.2.0.1.0       Production
TNS for 32-bit Windows: Version 9.2.0.1.0 - Production
NLSRTL Version 9.2.0.1.0 - Production
 
-- 1. 创建初始环境
-- 1.1 创建表test_job,存放执行日期
SQL> create table test_job ( a date );
 
表已创建。
 
-- 1.2 创建过程,向表test_job中插入执行时的日期
SQL> CREATE OR REPLACE PROCEDURE PRC_TEST_JOB(PRM_A        IN DATE,
  2                                           PRM_APPCODE  OUT NUMBER,
  3                                           PRM_ERRORMSG OUT VARCHAR2)
  4  IS
  5  BEGIN
  6    PRM_APPCODE := 1;
  7    PRM_ERRORMSG := 'succeed';
  8    INSERT INTO test_job VALUES(prm_a);
  9    COMMIT;
 10  END PRC_TEST_JOB;
 11
 12  /
 
过程已创建。
 
-- 2. 创建任务
-- 2.1 查看当前时间,用来确定创建的任务的执行时间
SQL> select to_char(sysdate, 'yyyy-mm-dd hh24:mi:ss') from dual;
 
TO_CHAR(SYSDATE,'YY
-------------------
2008-03-27 10:52:20
 
-- 2.2 创建任务,调用步骤1.2中创建的过程。在10:58:01时执行。
SQL> variable x number;
SQL> begin
  2    sys.dbms_job.submit(job => :x,
  3                        what => 'DECLARE
  4                                    n_appcode NUMBER(10);
  5                                    v_errormsg VARCHAR2(4000);
  6                                  BEGIN
  7                                    prc_test_job(SYSDATE, n_appcode, v_errormsg);
  8                                  END;',
  9                        next_date => to_date('27-03-2008 10:58:01', 'dd-mm-yyyy hh24:mi:ss'),
 10                        interval => 'last_day(add_months(sysdate, 1))');
 11    commit;
 12  end;
 13  /
 
PL/SQL 过程已成功完成。
 
-- 3. 验证数据
-- 3.1 在该执行时间之前没有数据
SQL> select * from test_job;
 
未选定行
 
SQL> select to_char(sysdate, 'yyyy-mm-dd hh24:mi:ss') from dual;
 
TO_CHAR(SYSDATE,'YY
-------------------
2008-03-27 10:57:46
 
-- 3.2 过了该时间后,可以查询到数据
SQL> select to_char(sysdate, 'yyyy-mm-dd hh24:mi:ss') from dual;
 
TO_CHAR(SYSDATE,'YY
-------------------
2008-03-27 10:58:18
 
SQL> select to_char(a, 'yyyy-mm-dd hh24:mi:ss') from test_job;
 
TO_CHAR(A,'YYYY-MM-
-------------------
2008-03-27 10:58:05
 
-- 3.3 任务执行完毕后,自动根据条件(INTERVAL)更新下次的执行时间(NEXT_DATE)
SQL> select PRIV_USER, LAST_DATE, NEXT_DATE, INTERVAL from user_jobs;
 
PRIV_USER                      LAST_DATE  NEXT_DATE  INTERVAL
------------------------------ ---------- ---------- ----------------------------------
TIANYC                         27-3月 -08 30-4月 -08 last_day(add_months(sysdate, 1))
SQL> select what from user_jobs;
 
WHAT
-------------------------------------------------------------------------------
DECLARE
                                  n_appcode NUMBER(10);
                                  v_errormsg VARCHAR2(4000);
                                BEGIN
                                  prc_test_job(SYSDATE, n_appcode, v_errormsg);
                                END;
 
SQL>

-- 4. 存在问题:
-- 4.1 test_job里的执行时间不是10:58:01, 而是10:58:05:
SQL> select to_char(a, 'yyyy-mm-dd hh24:mi:ss') from test_job;
 
TO_CHAR(A,'YYYY-MM-
-------------------
2008-03-27 10:58:05
 
-- 4.2 在视图user_jobs里也是如此。把这两个时间精确到秒,看看效果:
SQL> select PRIV_USER, to_char(LAST_DATE, 'yyyy-mm-dd hh24:mi:ss'),
 2   to_char(NEXT_DATE, 'yyyy-mm-dd hh24:mi:ss'), INTERVAL from user_jobs;
 
PRIV_USER  TO_CHAR(LAST_DATE,' TO_CHAR(NEXT_DATE,' INTERVAL
---------- ------------------- ------------------- --------------------------------
TIANYC     2008-03-27 10:58:05 2008-04-30 10:58:05 last_day(add_months(sysdate, 1))
          
SQL>
 
-- 4.3 经过多次测试,每次执行时并不是按照上次指定的秒执行的,而是滞后几秒。这样累计起来,就可能导致执行日期的错误。
--      所以要想每次都精确地按照秒来执行job,应该手工来指定时间。比如:
-- 4.3.1 在每个月的月底的午夜执行
SQL> select to_char(last_day(add_months(to_date(to_char(sysdate, 'yyyy-mm-dd'), 'yyyy-mm-dd'), 1)), 'yyyy-mm-dd hh24:mi:ss') from dual;
 
TO_CHAR(LAST_DAY(AD
-------------------
2008-04-30 00:00:00
 
-- 4.3.2 在每个月的月底的任意指定时间执行(这里我指定时间为11:24:31)
SQL> select to_char(last_day(add_months(to_date(to_char(sysdate, 'yyyy-mm-dd') || ' 11:24:31', 'yyyy-mm-dd hh24:mi:ss'), 1)), 'yyyy-mm-dd hh24:mi:ss') from dual;
 
TO_CHAR(LAST_DAY(AD
-------------------
2008-04-30 11:24:31
 
SQL>
 
阅读(4201) | 评论(3) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2009-02-16 17:42:34

厉害的 我转载到我空间了,可以多看多学习啊

chinaunix网友2008-04-03 10:52:55

真厉害呀,我什么能有你这水平就好了。

chinaunix网友2008-04-01 20:57:27

好厉害呀,我什么时候能到你这水平就好了。