在Oracle10g裏面,已經不同意使用DBMS_JOB,推薦使用DBMS_SCHEDULER,它的健壯爲調度提供更好的規劃和結構。下面我只簡單寫一個例子。 begin DBMS_SCHEDULER.CREATE_PROGRAM( program_name => 'guozhw', program_type => 'STORED_PROCEDURE', program_action => 'guozhwTest', number_of_arguments => 0, enabled => true, comments => '定時調度測試' ); end; program_name:program的名字 program_type:program的類型。STORED_PROCEDURE存儲過程 program_action:要執行的程序。guozhwTest 存儲過程的名字 舉例以下: BEGIN DBMS_SCHEDULER.CREATE_PROGRAM ( program_name => 'PRM_CREATEDUNDATA', program_action => 'PKG_SCHEDULER.CREATEDUNDATA', program_type => 'STORED_PROCEDURE', enabled => FALSE, number_of_arguments => 5, comments => '生成催繳數據' ); DBMS_SCHEDULER.DEFINE_PROGRAM_ARGUMENT(PROGRAM_NAME => 'PRM_CREATEDUNDATA', ARGUMENT_POSITION => 1, ARGUMENT_NAME => 'I_BILLINGCYCLEID', ARGUMENT_TYPE => 'VARCHAR2', DEFAULT_VALUE => NULL, OUT_ARGUMENT => FALSE); DBMS_SCHEDULER.DEFINE_PROGRAM_ARGUMENT(PROGRAM_NAME => 'PRM_CREATEDUNDATA', ARGUMENT_POSITION => 2, ARGUMENT_NAME => 'I_BUSINESSTYPE', ARGUMENT_TYPE => 'VARCHAR2', DEFAULT_VALUE => NULL, OUT_ARGUMENT => FALSE); DBMS_SCHEDULER.DEFINE_PROGRAM_ARGUMENT(PROGRAM_NAME => 'PRM_CREATEDUNDATA', ARGUMENT_POSITION => 3, ARGUMENT_NAME => 'I_STATE', ARGUMENT_TYPE => 'VARCHAR2', DEFAULT_VALUE => NULL, OUT_ARGUMENT => FALSE); DBMS_SCHEDULER.DEFINE_PROGRAM_ARGUMENT(PROGRAM_NAME => 'PRM_CREATEDUNDATA', ARGUMENT_POSITION => 4, ARGUMENT_NAME => 'I_BANKCODE', ARGUMENT_TYPE => 'VARCHAR2', DEFAULT_VALUE => NULL, OUT_ARGUMENT => FALSE); DBMS_SCHEDULER.DEFINE_PROGRAM_ARGUMENT(PROGRAM_NAME => 'PRM_CREATEDUNDATA', ARGUMENT_POSITION => 5, ARGUMENT_NAME => 'I_OPERATORCODE', ARGUMENT_TYPE => 'VARCHAR2', DEFAULT_VALUE => NULL, OUT_ARGUMENT => FALSE); DBMS_SCHEDULER.ENABLE(NAME => 'PRM_CREATEDUNDATA'); COMMIT; END; 以後就能夠在select * from sys.USER_SCHEDULER_PROGRAMS t中看到program了 ×××××××××××××××××××××××××××××××××××××××××*********************************************************** 最近遇到一個CASE,要作定時任務,想來想去,仍是先看看Oracle提供了啥.. Oracle10g引入了一種新的做業調度程序:dbms_scheduler,經過以下命令能夠查看它包含一些什麼功能, SQL> desc sys.dbms_scheduler; ... 不過,最好仍是用PL/SQL dev或者TOAD來展開包頭,這樣全部功能和註釋都一幕瞭然了。 在Oracle9i,咱們都是job來運行定時任務,例如定時備份一些數據,定時清理一下歸檔。在Oracle10g裏面,已經不 同意試用DBMS_JOB,而是推薦使用DBMS_SCHEDULER,它更加的複雜和健壯,爲調度提供更好的規劃和結構。 dbms_scheduler把建立一個做業分紅了不一樣的部分:dbms_scheduler,program和job,下面咱們依次來建立一個例子。 首先建立一個測試表: SQL> desc user118800; Name Type Nullable Default Comments ------------- ------------ -------- ------- -------- SN NUMBER 序列 AREACODE VARCHAR2(4) 區號 DEVICESNUMBER VARCHAR2(12) 電話號碼 READ INTEGER 0 是否讀取 BIT INTEGER 業務位 DDATE DATE Y 有以下存儲過程: create or replace procedure pro_test is begin UPDATE user118800 SET READ=1,ddate=SYSDATE WHERE READ=0 AND ROWNUM <2; COMMIT; --sys.dbms_lock.sleep(120); end pro_test; 這個測試,就是用過程來更新user118800表裏面的一行數據,並記錄下更新時間,接着咱們就建立做業來定時執行。 建立一個scheduler: /************************************************************* * Schedule Administration Procedures ************************************************************* */ -- Schedule attributes which can be used with set_attribute/get_attribute are : -- -- repeat_interval - VARCHAR2 -- an expression using the calendar syntax -- comments - VARCHAR2 -- an optional comment. -- end_date - TIMESTAMP WITH TIME ZONE -- cutoff date after which the schedule will not specify -- any dates -- start_date - TIMESTAMP WITH TIME ZONE -- start or reference date used by the calendar syntax -- -- Schedules cannot be enabled and disabled. -- Create a named schedule. This must be a valid schedule. PROCEDURE create_schedule( schedule_name IN VARCHAR2, start_date IN TIMESTAMP WITH TIME ZONE DEFAULT NULL, repeat_interval IN VARCHAR2, end_date IN TIMESTAMP WITH TIME ZONE DEFAULT NULL, comments IN VARCHAR2 DEFAULT NULL); 這是建立scheduler的存儲過程,運行以下: begin -- Call the procedure sys.dbms_scheduler.create_schedule(schedule_name => 'your schedule_name', start_date => '27-8月 -07 12.00.00.000 上午', repeat_interval => 'FREQ=MINUTELY; INTERVAL=1', end_date => '28-8月 -07 12.00.00.000 上午', comments => 'TEST SCHEDULER'); end; 這裏須要注意的是,參數start_date和end_date都是TIMESTAMP 類型,在輸入的時候要遵循它們的格式, 每每doc裏面的格式都是英文環境下的,這個時候你要經過查詢字典nls_database_parameters或者 select to_timestamp(sysdate) from dual; 來得到格式。 repeat_interval是調度運行的時間間隔,本例是每分鐘運行一次 repeat_interval => 'FREQ=HOURLY; INTERVAL=1' 每小時運行一次 repeat_interval => 'FREQ=DAILY INTERVAL=1' 天天運行一次 更詳細的說明,要查詢doc。另外它還有一個過程dbms_scheduler.evaluate_calendar_string是估算下一次運行的時間的。 第二步,建立一個program /************************************************************* * Program Administration Procedures ************************************************************* */ -- Program attributes which can be used with set_attribute/get_attribute are: -- -- program_action - VARCHAR2 -- This is a string specifying the action. In case of: -- 'PLSQL_BLOCK': PLSQL code -- 'STORED_PROCEDURE: name of the database object -- representing the type (optionally with schema). -- 'EXECUTABLE': Full pathname including the name of the -- executable, or shell script. -- program_type - VARCHAR2 -- type of program. This must be one of the supported -- program types. Currently these are -- 'PLSQL_BLOCK', 'STORED_PROCEDURE', 'EXECUTABLE' -- comments - VARCHAR2 -- an optional comment. This can describe what the -- program does, or give usage details. -- number_of_arguments- PLS_INTEGER -- the number of arguments of the program that can be set -- by any job using it, these arguments MUST be defined -- before the program can be enabled -- enabled - BOOLEAN -- whether the program is enabled or not. When the program -- is enabled, checks are made to ensure that the program -- is valid. -- Create a new program. The program name can be optionally qualified with a -- schema. If enabled is set to TRUE, validity checks will be performed and -- the program will be created in an enabled state if all are passed. PROCEDURE create_program( program_name IN VARCHAR2, program_type IN VARCHAR2, program_action IN VARCHAR2, number_of_arguments IN PLS_INTEGER DEFAULT 0, enabled IN BOOLEAN DEFAULT FALSE, comments IN VARCHAR2 DEFAULT NULL); 運行以下: begin -- Call the procedure sys.dbms_scheduler.create_program(program_name => 'your program_name', program_type => 'PLSQL_BLOCK', program_action => 'BEGIN PRO_TEST; END;', number_of_arguments => 0, enabled => TRUE, comments => 'comments'); end; 這裏須要注意的是program_type,若是你要運行PLSQL塊,則選擇PLSQL_BLOCK,若是是sh腳本,則'EXECUTABLE'。 這裏我選擇PLSQL塊。 第三步建立一個job -- create a job using a named schedule object and a named program object. -- If enabled is set TRUE, it will be attempted to enable this job after -- creating it. -- Values must be set for each argument of the program that does not have a -- default_value specified (before enabling the job). PROCEDURE create_job( job_name IN VARCHAR2, program_name IN VARCHAR2, schedule_name IN VARCHAR2, job_class IN VARCHAR2 DEFAULT 'DEFAULT_JOB_CLASS', enabled IN BOOLEAN DEFAULT FALSE, auto_drop IN BOOLEAN DEFAULT TRUE, comments IN VARCHAR2 DEFAULT NULL); 實際在10g提供的過程裏面,有6中create job的方法,並且也能夠單首創建做業,但這裏咱們選擇其中一種。 begin -- Call the procedure sys.dbms_scheduler.create_job(job_name => 'my test job', program_name => 'your program_name', schedule_name => 'your schedule_name', job_class => 'DEFAULT_JOB_CLASS', enabled => true, auto_drop => true, comments => 'comments'); end; 這樣就完成了一個job的建立,下面就是運行它: begin -- Call the procedure sys.dbms_scheduler.run_job(job_name => 'my test job', use_current_session => 'false'); end; use_current_session 這個參數要定義爲false,它才能在後臺運行。 建立之後的監控: 查看調度 SELECT * FROM User_Scheduler_Schedules; 查看做業 SELECT * FROM User_Scheduler_Jobs; 查看程序 SELECT * FROM User_Scheduler_Programs; 查看執行中的做業,也就是PRO_TEST在運行階段的做業。前面的過程當中,我定義了一個sys.dbms_lock.sleep(120); 實際就是爲了查看這個視圖。 SELECT * FROM User_Scheduler_Running_Jobs; 查看job的日誌 SELECT * FROM User_Scheduler_Job_Log; 若是日誌中有FAILeD的狀態,那麼就要查看altersid.log了。 好了,一個基本的調度例子就完成了。若是想更詳細的瞭解它,請閱讀該包頭的註釋,和查閱相關DOC Oracle® Database PL/SQL Packages and Types Reference 10g Release 2 (10.2) 在9i的時候,job有一個煩人的問題,就是延時,一個job的執行耗時是10分鐘,inteval爲1天,第一次執行爲晚上12點, 在之後的執行過程當中,該job爲逐漸按10分鐘遞增時間。10g的Scheduler解決了這個問題,可是我仍是發現Scheduler一樣沒法異步的執行,個人inteval是1分鐘,但在程序中若是打開 sleep(120)後,實際inteval就變成了120秒...它仍是和job同樣,必須等待上一次任務的完成,這樣也沒有達到個人初衷。 最後,我也應用中是否最終會使用Scheduler,還有待測試。除了Scheduler,在中間層,如EJB和Spring.... part 1 1. 建立job job是什麼呢? 簡單的說就是計劃(schedule)加上任務說明. 另外還有一些必須的參數. 這裏提到的"任務"能夠是數據庫內部的存儲過程,匿名的PL/SQL塊,也能夠是操做系統級別的腳本. 能夠有兩種方式來定義"計劃": 1) 使用DBMS_SCHDULER.CREATE_SCHEDULE 定義一個計劃; 2) 調用DBMS_SCHDULER.CREATE_JOBE過程直接指定 (下面會詳細說明) 在建立一個計劃時,你至少須要指定下面的屬性,它們是job運行所必須的: 開始時間 (start_time); 重複頻率 (repeat_interval); 結束時間 (end_time) 另外,對於一個job而言,還有不少的附加參數: job_class job_priority auto_drop restartable max_runs max_failures schedule_limit logging_level 下面,我以問答的形式來具體解釋. Q1:怎麼從數據庫中查詢job的屬性 ? A1: 有兩種方法: 1) 查詢(DBA|ALL|USER)_SCHEDULER_JOBS 視圖 (提示: 根據用戶權限的不一樣,選擇性的查詢 DBA|ALL|USER視圖) 2) 調用DBMS_SCHEDULER包中的GET_ATTRIBUTE 過程 Q2: 怎麼設置這些屬性呢? A2: 也是有兩種方法 1) 在建立job時直接指定 2) 調用DBMS_SCHEDULER包中的SET_ATTRIBUTE 過程 Q3: "我須要什麼權限才能建立job" ? A3: 你至少須要create_job這個系統權限。若是用戶擁有create any job這個權限, 它能夠建立屬主爲任何用戶(SYS用戶除外)的job. 缺省狀況下,job會被建立在當前的schema下,而且是沒有激活的; 若是要使job一建立 就自動激活,須要顯式的設置enabled 屬性爲true, 來看一個例子: begin dbms_scheduler.create_job ( job_name => 'ARC_MOVE', schedule_name => 'EVERY_60_MINS', job_type => 'EXECUTABLE', job_action => '/home/dbtools/move_arcs.sh', enabled => true, comments => 'Move Archived Logs to a Different Directory' ); end; / Q4: 能不能詳細地講述一下上面這個過程用到的各個參數? A4: job_name: 顧名思義,每一個job都必須有一個的名稱 schedule_name: 若是定義了計劃,在這裏指定計劃的名稱 job_type: 目前支持三種類型: PL/SQL塊: PLSQL_BLOCK, 存儲過程: STORED_PROCEDURE 外部程序: EXECUTABLE (外部程序能夠是一個shell腳本,也能夠是操做系統級別的指令). job_action: 根據job_type的不一樣,job_action有不一樣的含義. 若是job_type指定的是存儲過程,就須要指定存儲過程的名字; 若是job_type指定的是PL/SQL塊,就須要輸入完整的PL/SQL代碼; 若是job_type指定的外部程序,就須要輸入script的名稱或者操做系統的指令名 enabled: 上面已經說過了,指定job建立完畢是否自動激活 comments: 對於job的簡單說明 2. 指定job的執行頻率 若是咱們建立了一個job,而且但願它按照咱們指定的日期和時間來運行,就須要定義 job的重複頻度了. 例如天天運行,每週日的22:00運行, 每週一,三,五運行,每一年的 最後一個星期天運行等等. (說明:10G之前的版本,與操做系統的交互方面,實現的不是很好。例如要實現一個 按期的rman備份任務,就須要結合OS的命令來實現,在UNIX下能夠用crontab實現, 在windows下用AT命令來實現) 10G 在這方面有了很大的加強,由於建立job時能夠直接指定操做系統的命令或者 腳本,再合理的定義job的執行頻率,能夠很輕鬆地完成複雜的調度任務. 10G 支持兩種模式的repeat_interval,一種是PL/SQL表達式,這也是dbms_job包 中所使用的,例如SYSDATE+1, SYSDATE + 30/24*60; 另外一種就是日曆表達式。 例如MON表示星期一,SUN表示星期天,DAY表示天天,WEEK表示每週等等. 下面來 看幾個使用日曆表達式的例子: repeat_interval => 'FREQ=HOURLY; INTERVAL=2' 每隔2小時運行一次job repeat_interval => 'FREQ=DAILY' 天天運行一次job repeat_interval => 'FREQ=WEEKLY; BYDAY=MON,WED,FRI" 每週的1,3,5運行job repeat_interval => 'FREQ=YEARLY; BYMONTH=MAR,JUN,SEP,DEC; BYMONTHDAY=30' 每一年的3,6,9,12月的30號運行job 用過crontab的人應該都有種似曾相識的感受吧,呵呵 下面再說說使用日曆表達式的規則: 日曆表達式基本分爲三部分: 第一部分是頻率,也就是"FREQ"這個關鍵字, 它是必須指定的; 第二部分是時間間隔,也就是"INTERVAL"這個關鍵字, 取值範圍是1-999. 它是可選的參數; 最後一部分是附加的參數,可用於 精確地指定日期和時間,它也是可選的參數,例以下面這些值都是合法的: BYMONTH,BYWEEKNO,BYYEARDAY,BYMONTHDAY,BYDAY BYHOUR,BYMINUTE,BYSECOND 詳細的參數說明請參考 dbms_scheduler的使用說明. 既然說到了repeat_interval,你可能要問:"有沒有一種簡便的方法來得出, 或者說是評估出job的每次運行時間,以及下一次的運行時間呢?" dbms_scheduler包提供了一個過程evaluate_calendar_string,能夠很 方便地完成這個需求. 來看下面的例子: SQL> set serveroutput on size 999999 SQL> declare L_start_date TIMESTAMP; l_next_date TIMESTAMP; l_return_date TIMESTAMP; begin l_start_date := trunc(SYSTIMESTAMP); l_return_date := l_start_date; for ctr in 1..10 loop dbms_scheduler.evaluate_calendar_string( 'FREQ=DAILY; BYDAY=MON,TUE,WED,THU,FRI; BYHOUR=7,15', l_start_date, l_return_date, l_next_date ); dbms_output.put_line('Next Run on: ' || to_char(l_next_date,'mm/dd/yyyy hh24:mi:ss') ); l_return_date := l_next_date; end loop; end; / 輸出結果以下: Next Run on: 03/22/2004 07:00:00 Next Run on: 03/22/2004 15:00:00 Next Run on: 03/23/2004 07:00:00 Next Run on: 03/23/2004 15:00:00 Next Run on: 03/24/2004 07:00:00 Next Run on: 03/24/2004 15:00:00 Next Run on: 03/25/2004 07:00:00 Next Run on: 03/25/2004 15:00:00 Next Run on: 03/26/2004 07:00:00 Next Run on: 03/26/2004 15:00:00 DBMS_SCHEDULER 中度解析: part 2 3. 建立程序 (program) 什麼是程序? 個人理解就是準備計劃須要的元數據(metadata),它 包括如下部分: 程序名; 程序中用到的參數: 例如程序的類型,以及具體操做的描述 來看一個例子 begin dbms_scheduler.create_program( program_name=> 'DAILY_BACKUP_SH', program_type=> 'EXECUTABLE', program_action=> '/home/oracle/script/daily_backup.sh'); end; / 這個例子將會建立一個名爲"DAILY_BACKUP_SH"的程序,類型是可執行的shell腳本,腳本的名稱是「/home/oracle/script/daily_backup.sh」 Q1: 程序和做業相比,有什麼區別呢? A1: 程序實際上是能夠與做業分離的,所以不一樣的用戶能夠在不一樣的時間段去重用它.而一個做業是屬於特定的用戶的; 另外,將程序與做業分離,也就激活了一個新的程序庫(Program Library),利用程序庫,用戶能夠很自由地選擇特定的程序在特定的時間段運行,以及自由的配置程序執行時的參數. Q2: 可否解釋一下 create_program與create_job的關係? A2: 首先,你應該知道建立程序並非一個計劃的必須組成部分,一個計劃能夠沒有程序,可是必須有一個已經定義好的做業; 另外,program_action這個參數也是可選的,假如程序的類型是pl/sql 塊,你徹底能夠在建立做業時來指定它. 上面已經提到了,程序和做業能夠是分離的,這樣一個程序的具體執行(ACTION) 就能夠靈活地肯定。它既能夠只運行一次,也能夠在多個不一樣的做業中來重用這個執行. 這樣一來,在修改針對一個做業的計劃時就很是靈活,你不須要從新建立pl/sql塊. Q3: 運行 create_program須要什麼權限 ? A3: 要保證create_program可以順利執行,你一樣須要CREATE JOB這個系統權限. 若是一個用戶擁有了create any job這個權限,它就能夠建立屬主爲任何用戶的程序(SYS用戶除外) 與建立做業同樣,一個程序創建完畢,缺省的狀態也是非激活的,固然你能夠在建立程序時,顯式的設置enabled參數爲true來 激活它. Q4: 可否介紹一下create_program這個過程的各個參數? 更多的例子? A4: program_name: 指定程序的名稱; program_type: 目前只支持下面三種: STORED_PROCEDURE PLSQL_BLOCK EXECUTABLE program_action: (有長度限制,由於類型是varchar2) 存儲過程的名稱; 具體的pl/sql代碼 操做系統腳本名稱 來看一個使用pl/sql塊的例子 BEGIN DBMS_SCHEDULER.CREATE_PROGRAM( program_name => 'LEO.UPDATE_STATS', program_type => 'PLSQL_BLOCK', program_action => 'DECLARE sUsername varchar2(30); cursor cur is select username from dba_users where username not in ('SYS','SYSTEM','SYSMAN','DBSNMP') and account_status='OPEN' and substr(username,1,5)<>'MGMT_' ; BEGIN OPEN cur; FETCH cur into sUsername; WHILE cur%Found LOOP DBMS_STATS.GATHER_SCHEMA_STATS (sUsername); FETCH cur into sUsername; END LOOP; close cur; END;'); END; / 上面這個例子建立一個名爲"UPDATE_STATS"的程序,它的類型是PL/SQL 塊,完成更新非系統用戶的統計信息的工做。在這個基礎上你能夠定製一個合理的計劃,來按期執行這個程序. 4. 配置程序的參數 先來看一個例子 begin dbms_scheduler.create_program( program_name=> 'LEO.UPDATE_STATS_2', program_action=> 'LEO.UPDATE_SCHEMA_STATUS'); program_type=> 'STORED_PROCEDURE', number_of_arguments => 1, enabled => TRUE); dbms_scheduler.define_program_argument( program_name=> 'UPDATE_STATS_2', argument_name => 'SCHEMA_NAME', argument_position => 1, argument_type => 'varchar2', default_value => 'HR'); end; / 這個例程是否是很像上面的那個例子呢? 對,它們的區別只是 在這個例子中,程序的類型是存儲過程,而不是pl/sql塊. 解釋: 使用define_program_argument這個過程來定義一個程序所須要的參數. 有兩點說明一下: 1) 程序若是使用了參數,就必須事先指定,這樣才能在程序被job使用時生效; 2) 定義程序的參數不會改變程序的激活屬性。也就是說,若是一個程序是沒有激活的狀態,運行了define_program_argument過程不會自動激活這個程序. 關於權限: 缺省狀況下只有program的owner才能修改建立的程序,若是用戶被授予了alter 權限 或者 create any job權限,就能夠修改屬主爲另外一個用戶的程序. 5. 建立計劃(Schedule) 其實,若是你已經瞭解了怎樣建立做業和程序,就等於已經掌握怎樣建立計劃了。你須要作的附加工做只是指定計劃的開始時間,結束時間,重複頻率等等. 來看一個例子 begin dbms_scheduler.create_job( job_name=> 'leo.UPDATE_STATS_JOB', program_name=> 'leo.UPDATE_STATS_2', start_date=>'2005-06-20 11:00.00.000000 PM +8:00', repeat_interval=>'FREQ=MONTHLY;INTERVAL=1', end_date=>'2006-06-20 11:00.00.000000 PM +8:00', comments=>'Monthly statistics collection job'); end; / start_date和end_date這兩個參數須要說明一下: 它們的數據類型 是timestamp,所以須要精確的指定時間和時區. 時間格式繼承的是NLS_DATE_FORMAT這個初始化參數的值. 下一部分介紹: 1) 配置做業的參數; 2) 建立/使用/管理做業,程序,計劃所須要的系統權限. 一些補充 1.在10G R2 中, 若是用dbms_scheduler建立的job的類型爲executable, 須要create external job 權限。 2. 在使用create_job或者create_schedule前,請先檢查 NLS_DATE_LANGUAGE, NLS_DATE_FORMAT, NLS_TIMESTAMP_FORMAT, NLS_TIMESTAMP_TZ_FORMAT 等參數的值, 經過alter session 命令來修改 例如: SQL> select * from nls_session_parameters; alter session set NLS_DATE_LANGUAGE='AMERICAN'; alter session set NLS_DATE_FORMAT='dd-mm-yyyy hh24:mi:ss'; alter session set NLS_TIMESTAMP_TZ_FORMAT='dd-mm-yyyy HH:MI:SS.FF AM TZR' begin dbms_scheduler.create_job( job_name=> 'zip_emlog', job_type=> 'EXECUTABLE', job_action =>'/home/leo/zip_log.sh', enabled=>true, start_date=>'03-07-2005 9:30:00 PM + 8:00', repeat_interval=>'FREQ=MINUTELY;INTERVAL=30', end_date=>'31-07-2005 9:30:00 PM + 8:00', comments=>'Get a latest em log copy and compress it every 30 minutes'); end; /