dbms_scheduler是oracle 10g起官方主推且必须使用的调度工具,用于替代已废弃的dbms_job;其最简每日定时作业需严格定义三要素:唯一job_name、明确job_type与job_action、符合icalendar语法的repeat_interval(如'freq=daily;byhour=1;byminute=0;bysecond=0'),并注意start_date为首次执行时间、时区需显式指定。
dbms_scheduler 是 oracle 10g 起官方主推的调度工具,替代老旧的 dbms_job。它不是“能用”,而是“必须用”——尤其在 12c/19c 及以后版本中,dbms_job 已被标记为废弃(deprecated),长期维护项目应直接上 dbms_scheduler。
怎么写最简可用的每日定时作业
核心是三要素:作业名、执行动作、重复规则。缺一不可,且顺序敏感。
-
job_name必须全局唯一(同一 schema 下),建议带业务前缀,比如'JOB_PROC_LOAD_DAILY' -
job_type推荐用'STORED_PROCEDURE'(直调存储过程)或'PLSQL_BLOCK'(需写完整 BEGIN/END 块) -
job_action若选STORED_PROCEDURE,值就是存储过程名(如'PROC_LOAD_DATA'),不能带括号、参数或分号 -
repeat_interval使用 iCalendar 语法,常见错误是漏掉FREQ=或写错大小写(必须全大写)
BEGIN
DBMS_SCHEDULER.CREATE_JOB (
job_name => 'JOB_PROC_LOAD_DAILY',
job_type => 'STORED_PROCEDURE',
job_action => 'PROC_LOAD_DATA',
start_date => TRUNC(SYSDATE) + 1/24, -- 明天凌晨1点开始(SYSDATE 是当前时间,+1/24 = +1小时)
repeat_interval => 'FREQ=DAILY; BYHOUR=1; BYMINUTE=0; BYSECOND=0',
enabled => TRUE,
comments => '每日凌晨1点执行数据装载'
);
END;
/
注意:start_date 是首次执行时间,不是“创建时间”。如果设成过去时间,作业会立即触发一次(哪怕已过期),再按规则走下一轮。
为什么存储过程带参数时得用 PLSQL_BLOCK
DBMS_SCHEDULER 的 STORED_PROCEDURE 类型不支持传参。想传 startdate 和 enddate?只能切到 PLSQL_BLOCK,把调用逻辑包进匿名块里。
常见坑:
- 匿名块末尾的
/是 SQL*Plus / SQLcl 必需的执行符,漏掉就卡住不报错 - 字符串里嵌单引号要双写(
''),否则语法报错 -
TO_CHAR(SYSDATE, 'YYYYMMDD')这类表达式必须在块内执行,不能写在job_action字符串外
BEGIN
DBMS_SCHEDULER.CREATE_JOB (
job_name => 'JOB_PROC_MONTHLY',
job_type => 'PLSQL_BLOCK',
job_action => 'BEGIN PROC_HIGH_SETTLE_REP_MONTH(
TO_CHAR(ADD_MONTHS(SYSDATE,-1),''YYYYMM'')||''01'',
TO_CHAR(LAST_DAY(ADD_MONTHS(SYSDATE,-1)),''YYYYMMDD'')
); END;',
start_date => TRUNC(ADD_MONTHS(SYSDATE,-1), 'MM') + INTERVAL '5' DAY,
repeat_interval => 'FREQ=MONTHLY; BYMONTHDAY=5; BYHOUR=23; BYMINUTE=8;',
enabled => TRUE
);
END;
/
这个例子对应 crontab 场景里的“每月5号23:08执行”,但完全在数据库内完成,不依赖外部 shell 环境。
查不到作业?先确认权限和视图范围
刚建完作业却查不到,大概率是权限或视图选错:
-
USER_SCHEDULER_JOBS:只查当前用户创建的作业(最常用) -
ALL_SCHEDULER_JOBS:查当前用户有权限看到的所有作业(含别人授权给你的) -
DBA_SCHEDULER_JOBS:查全库作业(需 DBA 权限)
常见权限缺失现象:
- 执行
CREATE_JOB报ORA-27486: insufficient privileges→ 缺CREATE JOB系统权限 - 作业状态一直是
DISABLED→ 创建时enabled => FALSE没改,或CREATE_JOB后没显式ENABLE - 存储过程执行报
ORA-06576: not a valid procedure or function name→job_action写错名,或目标过程不在当前 schema,且没加 schema 前缀(如'SCHEMA_NAME.PROC_NAME')
检查命令示例:
SELECT job_name, enabled, state, last_start_date, next_run_date FROM USER_SCHEDULER_JOBS WHERE job_name LIKE '%DAILY%';
SELECT log_date, status, error# FROM USER_SCHEDULER_JOB_LOG WHERE job_name = 'JOB_PROC_LOAD_DAILY' AND ROWNUM
作业跑失败了,日志在哪看、怎么重试
DBMS_SCHEDULER 默认记录运行日志,但只保留最近几轮(默认 30 天,可调)。关键字段是 status(SUCCEEDED/FAILED)和 error#(Oracle 错误号,如 ORA-01403)。
重试不是自动的——除非你显式配置:
-
max_runs:最多执行几次(默认 NULL,无限) -
max_failures:连续失败几次后停掉(默认 NULL) -
restartable:设为TRUE才允许作业崩溃后从断点续跑(仅对某些job_type有效)
更实用的做法是手动补跑:
EXEC DBMS_SCHEDULER.RUN_JOB('JOB_PROC_LOAD_DAILY', use_current_session => FALSE);
use_current_session => FALSE 表示另起一个会话执行,避免阻塞当前连接;若设为 TRUE,则同步等待,适合调试。
真正容易被忽略的是时区问题:start_date 和 repeat_interval 默认按数据库时区(DBTIMEZONE)解析。如果服务器跨时区部署,或需要按北京时间统一调度,务必在 CREATE_JOB 时显式指定:
start_date => FROM_TZ(CAST(TRUNC(SYSDATE) + 1/24 AS TIMESTAMP), 'Asia/Shanghai')
否则凌晨1点可能变成 UTC 时间的1点,差出8小时。











