oracle存储过程无法自行定时,必须使用dbms_scheduler;创建job后必须显式启用(enabled=>true),repeat_interval需严格遵循日历语法,带参调用须用wrapper过程或plsql_block封装。

DBMS_JOB 是旧方案(已弃用),DBMS_SCHEDULER 是当前唯一推荐路径。别在存储过程里写 SLEEP 或循环等待,那不是定时,是阻塞。
DBMS_SCHEDULER.create_job 必须显式启用才跑得起来
创建完 job 不等于它会自动执行。默认状态是 DISABLED,NEXT_RUN_DATE 为 NULL,查 DBA_SCHEDULER_JOBS 就能看到。常见错误是漏掉 enabled => TRUE 参数:
- 错:只写
dbms_scheduler.create_job(job_name=>'j_daily', job_type=>'STORED_PROCEDURE', job_action=>'p_daily_report') - 对:必须加
enabled => TRUE,或创建后补dbms_scheduler.enable('j_daily') - 如果同名 job 已存在,会报
ORA-27477;建议先dbms_scheduler.drop_job或用begin ... exception when others then null; end;包裹
repeat_interval 字符串容错极低,大小写/空格都致命
repeat_interval 不是普通字符串,是 Oracle 内部解析的日历表达式。写错一个空格、大小写不一致、多一个分号,job 就卡在 CHAIN_STALLED 或直接报 ORA-27450。
- 合法:
'FREQ=DAILY; BYHOUR=2; BYMINUTE=0' - 非法:
'freq=daily;byhour=2'(小写)、'FREQ=DAILY ; BYHOUR=2'(空格)、'FREQ=DAILY;BYHOUR=2;'(末尾分号) - 调试建议:用
dbms_scheduler.evaluate_calendar_string验证,传入表达式和当前时间,看能否算出下一个触发点 - 避免手写:每月 5 号凌晨 2 点就用标准格式,别拼
TO_CHAR(SYSDATE, 'YYYYMMDD') = '20260905'
带参数的存储过程不能直传,得用命名程序封装
job_action 只接受无参对象名,比如 'p_daily_report'。如果你的存储过程要传 p_month VARCHAR2,直接写会报 ORA-27477 或运行时报 PLS-00306。
- 方案一:建 wrapper 过程,内部调用带参原过程:
CREATE OR REPLACE PROCEDURE p_daily_report_wrap AS BEGIN p_daily_report('202609'); END; - 方案二:改用
PLSQL_BLOCK类型,把调用语句写进字符串:job_action => 'BEGIN p_daily_report(''202609''); END;' - 注意单引号转义:字符串里用两个单引号表示一个字面单引号,否则编译失败
- 别用动态拼接(如
|| v_month ||),job 创建时就固化了值,没法每次取最新
别混淆 DBMS_JOB 和 DBMS_SCHEDULER,它们互不兼容
还在用 PL/SQL Developer 图形界面点 DBMS_Jobs 新建?那是基于 DBMS_JOB 的老接口,Oracle 从 10g 起就标记为 deprecated,23c 已彻底移除。新环境里它可能根本不可见,或创建后不触发。
-
DBMS_JOB依赖interval表达式(如'sysdate+1/24'),本质是轮询,精度差、资源占用高 -
DBMS_SCHEDULER支持日历语法、依赖链、事件触发、作业类(job class)控制资源,是唯一现代方案 - 查任务状态别用
user_jobs,改查user_scheduler_jobs和user_scheduler_job_log - 图形工具(如 PL/SQL Dev)的新建向导若仍引导你选
DBMS_JOB,请手动切到 SQL 模式写create_job
enabled => TRUE 不是可选项,是开关;repeat_interval 不是自由文本,是严格语法;带参调用不是加个括号就行,得绕一层。










