必须用PLSQL_BLOCK类型或PROGRAM+ARGUMENT方式传参,因job_action不支持带参数的存储过程调用语法,否则报ORA-27469;PLSQL_BLOCK中表达式创建时求值,PROGRAM路线才支持每次运行动态注入参数。
直接用 CREATE_JOB 调用带参存储过程会报 ORA-27469
不能把 proc_high_settle_rep_month(p_startdate => '20260601', p_enddate => '20260630') 直接写进 job_action。oracle 会拒绝解析,报错 ora-27469: job_action must be a valid pl/sql block or stored procedure name。原因很明确:job_action 只接受「纯名称」或「完整匿名块」,不支持带参数调用语法。
常见错误写法:
BEGIN
DBMS_SCHEDULER.CREATE_JOB(
job_name => 'monthly_report_job',
job_type => 'STORED_PROCEDURE',
job_action => 'proc_high_settle_rep_month(''20260601'', ''20260630'')', -- ❌ 错误!字符串不是合法 procedure 名
repeat_interval => 'FREQ=MONTHLY; BYMONTHDAY=1;',
enabled => TRUE
);
END;正确路径只有两条:
- 改用
PLSQL_BLOCK类型,把整个调用包进 BEGIN/END; - 拆成
PROGRAM+ARGUMENT+JOB三步,用SET_JOB_ARGUMENT_VALUE动态赋值。
用 PLSQL_BLOCK 类型传参最简单但时间值会“冻结”
如果参数是固定值(比如每月 1 号跑上月数据),或者能用 SYSDATE、ADD_MONTHS 等函数动态生成,PLSQL_BLOCK 是最快方案。但要注意:块里写的表达式在 job 创建时只计算一次,后续每次执行都复用这个“快照值”,不是实时重算。
例如下面这个 job,TO_CHAR(SYSDATE, 'YYYYMMDD') 在创建时就算出结果(比如 '20260623'),之后每天跑都传同一个日期:
BEGIN
DBMS_SCHEDULER.CREATE_JOB(
job_name => 'daily_log_job',
job_type => 'PLSQL_BLOCK',
job_action => 'BEGIN proc_log_data(TO_CHAR(SYSDATE, ''YYYYMMDD''), ''DAILY''); END;',
start_date => SYSTIMESTAMP,
repeat_interval => 'FREQ=DAILY; BYHOUR=2; BYMINUTE=0;',
enabled => TRUE
);
END;若需每次执行都取当前时间,必须显式写成函数调用形式,例如:
job_action => 'BEGIN proc_log_data(TO_CHAR(SYSDATE, ''YYYYMMDD''), ''DAILY''); END;'
而不是拼成字符串常量。
用 PROGRAM + DEFINE_PROGRAM_ARGUMENT 实现真正动态参数
当参数需要由调度器在每次运行时注入(比如自动算出上月起止日),必须走 PROGRAM 路线。核心步骤是:先建 program,定义参数个数和类型,再建 job 关联它,最后用 SET_JOB_ARGUMENT_VALUE 绑定值——注意,这个绑定不是一次性动作,而是为 job 设置“默认参数值”,调度器会在每次触发时自动代入。
关键点:
-
number_of_arguments必须与存储过程签名严格一致; -
DEFINE_PROGRAM_ARGUMENT中的ARGUMENT_POSITION从 1 开始编号,不能跳号; - program 创建时设
enabled => FALSE,等参数定义完再ENABLE; - job 创建时只指定
program_name,不再填job_action或number_of_arguments。
示例(为每月 1 日零点执行的报表任务设置动态参数):
BEGIN
DBMS_SCHEDULER.CREATE_PROGRAM(
program_name => 'monthly_report_prog',
program_type => 'STORED_PROCEDURE',
program_action => 'proc_high_settle_rep_month',
number_of_arguments => 2,
enabled => FALSE
);
<p>DBMS_SCHEDULER.DEFINE_PROGRAM_ARGUMENT(
program_name => 'monthly_report_prog',
argument_position => 1,
argument_name => 'p_startdate',
argument_type => 'VARCHAR2'
);</p><p>DBMS_SCHEDULER.DEFINE_PROGRAM_ARGUMENT(
program_name => 'monthly_report_prog',
argument_position => 2,
argument_name => 'p_enddate',
argument_type => 'VARCHAR2'
);</p><p>DBMS_SCHEDULER.ENABLE('monthly_report_prog');</p><p>DBMS_SCHEDULER.CREATE_JOB(
job_name => 'monthly_report_job',
program_name => 'monthly_report_prog',
start_date => TIMESTAMP '2026-07-01 00:00:00',
repeat_interval => 'FREQ=MONTHLY; BYMONTHDAY=1;',
enabled => TRUE
);</p><p>-- 设置默认参数值(可选;也可留空,运行时再 set)
DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE(
job_name => 'monthly_report_job',
argument_position => 1,
argument_value => TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -1), 'YYYYMMDD')
);
DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE(
job_name => 'monthly_report_job',
argument_position => 2,
argument_value => TO_CHAR(LAST_DAY(ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -1)), 'YYYYMMDD')
);
END;权限和调试最容易被忽略的三个点
很多 job 创建成功却静默失败,问题往往不在逻辑而在环境配置:
- 用户必须有
CREATE JOB权限,若调用其他 schema 的存储过程,还需EXECUTE权限(如GRANT EXECUTE ON other_schema.proc_high_settle_rep_month TO your_user); - job 默认写日志到
USER_SCHEDULER_JOB_RUN_DETAILS,但若没开日志(logging_level => DBMS_SCHEDULER.LOGGING_OFF),失败时连错误码都看不到; - 用
DBMS_SCHEDULER.RUN_JOB('job_name', use_current_session => FALSE)手动触发测试时,务必加use_current_session => FALSE,否则事务上下文可能污染实际调度行为。
查错优先看这张表:SELECT job_name, status, error#, error_msg FROM USER_SCHEDULER_JOB_RUN_DETAILS WHERE job_name = 'your_job' ORDER BY log_date DESC。status 是 SUCCEEDED 还是 FAILED,error_msg 里有没有 ORA- 编号,比猜逻辑快得多。


















