dbms_scheduler 是 oracle 10g 起的强制标准,dbms_job 在 12c/19c 已弃用;create_job 必须显式指定时区和 start_date,否则跨时区部署易导致任务错时;传参必须用 plsql_block;repeat_interval 遵循 icalendar 大写语法;查错优先看 user_scheduler_job_log 和 job_run_details。
dbms_scheduler 不是“可选替代”,而是 oracle 10g 起的强制标准——dbms_job 在 12c/19c 中已被标记为 deprecated,新项目直接用 dbms_scheduler,否则上线后可能被 dba 拒绝部署。
为什么 CREATE_JOB 必须显式指定时区和 start_date
Oracle 默认把 start_date 解析为数据库时区(DBTIMEZONE),但多数生产库跨地域部署,且调度规则(如 repeat_interval)依赖绝对时间点。若不显式指定时区,凌晨 1 点可能变成 UTC 时间、本地时间或会话时区时间,导致任务提前或漏跑。
-
start_date是首次执行时间,不是创建时间;设成过去时间(如SYSDATE - 1)会立刻触发一次,再按规则走下一轮 - 推荐写法:
start_date => FROM_TZ(CAST(TRUNC(SYSDATE) + 1/24 AS TIMESTAMP), 'Asia/Shanghai') - 避免用
SYSDATE直接加减,它返回的是DATE类型,无时区信息,隐式转换易出错
存储过程带参数时必须用 PLSQL_BLOCK,不能硬塞进 job_action
job_type => 'STORED_PROCEDURE' 的 job_action 只接受纯过程名(如 'PROC_LOAD_DATA'),任何括号、参数、分号都会报 ORA-27469: invalid procedure name。想传 startdate 和 enddate?只能切到 PLSQL_BLOCK。
- 正确写法:
job_action => 'BEGIN PROC_LOAD_DATA(TO_DATE(''20260515'', ''YYYYMMDD''), TO_DATE(''20260515'', ''YYYYMMDD'')); END;' - 注意单引号要双写(SQL 字符串转义),且整个块必须以分号结尾
- 如果参数来自动态时间(如“昨天”),用
TRUNC(SYSDATE) - 1替代硬编码日期,避免作业失效
iCalendar 语法里 FREQ=DAILY 写错大小写或漏字段就静默失败
repeat_interval 用的是 iCalendar 标准(RFC 5545),不是 cron 表达式。常见错误是写成小写 freq=daily 或漏掉 FREQ=,此时 CREATE_JOB 不报错,但作业状态永远是 DISABLED,日志里也查不到失败记录。
- 必须全大写:
'FREQ=DAILY; BYHOUR=2; BYMINUTE=30; BYSECOND=0' - 分钟级调度要补
BYSECOND,否则默认为 0 秒,可能错过整点前 59 秒的窗口 - 验证方法:查
USER_SCHEDULER_JOBS的repeat_interval字段是否原样存入,以及state是否为ENABLED
作业跑失败了,去哪查日志最有效
别只看 USER_SCHEDULER_JOBS 的 state,它只反映当前状态(比如 STOPPED 或 CHAIN_STALLED)。真正定位问题得查运行日志:
- 历史执行概览:
SELECT job_name, status, log_date, error# FROM USER_SCHEDULER_JOB_LOG WHERE job_name = 'JOB_PROC_LOAD_DAILY' ORDER BY log_date DESC - 详细错误堆栈:
SELECT * FROM USER_SCHEDULER_JOB_RUN_DETAILS WHERE job_name = 'JOB_PROC_LOAD_DAILY' AND status = 'FAILED' ORDER BY log_date DESC - 注意:
error#是 Oracle 错误号(如6550对应 PL/SQL 编译错误),结合additional_info字段才能定位具体哪行出错
最容易被忽略的是:作业启用后,第一次执行失败不会自动重试,也不会发告警——除非你显式配置 max_failures 和 restartable 属性。线上任务上线前,务必手动 EXEC DBMS_SCHEDULER.RUN_JOB('JOB_NAME') 测试一次,确认日志里有 SUCCESS 记录再放行。











