存储过程在 oracle数据库管理工具里定时自动化运行方案

Oracle数据库提供了多种方式实现存储过程的定时自动化运行。

以下是几种常用的方案:


方案1:使用 Oracle Scheduler(推荐)

这是Oracle 10g及以上版本内置的作业调度器,功能最强大、最稳定。

1.1 创建Scheduler Job(最简单)

复制代码
BEGIN
    -- 创建每天凌晨1点执行的Job
    DBMS_SCHEDULER.CREATE_JOB (
        job_name        => 'JOB_LOAD_STK_INDX_DAILY',
        job_type        => 'PLSQL_BLOCK',
        job_action      => 'BEGIN PROC_LOAD_STK_MKT_QUOT_INDX; END;',
        start_date      => SYSTIMESTAMP,
        repeat_interval => 'FREQ=DAILY; BYHOUR=1; BYMINUTE=0; BYSECOND=0',
        enabled         => TRUE,
        comments        => '每天凌晨1点执行股票指标数据装载'
    );
END;
/

1.2 更灵活的调度配置

复制代码
BEGIN
    -- 删除已存在的Job(如果需要重建)
    BEGIN
        DBMS_SCHEDULER.DROP_JOB('JOB_LOAD_STK_INDX_DAILY', TRUE);
    EXCEPTION
        WHEN OTHERS THEN NULL;
    END;
    
    -- 创建复杂的调度Job
    DBMS_SCHEDULER.CREATE_JOB (
        job_name        => 'JOB_LOAD_STK_INDX_DAILY',
        job_type        => 'PLSQL_BLOCK',
        job_action      => 'BEGIN 
                               DBMS_OUTPUT.PUT_LINE(''开始执行股票指标装载...'' || TO_CHAR(SYSDATE, ''YYYY-MM-DD HH24:MI:SS''));
                               PROC_LOAD_STK_MKT_QUOT_INDX;
                               DBMS_OUTPUT.PUT_LINE(''执行完成!'');
                           END;',
        start_date      => TRUNC(SYSDATE) + INTERVAL '1' HOUR,  -- 今天凌晨1点开始
        repeat_interval => 'FREQ=DAILY; INTERVAL=1',  -- 每天执行
        enabled         => FALSE,  -- 先不启用
        comments        => '股票指标数据自动化装载作业'
    );
    
    -- 启用Job
    DBMS_SCHEDULER.ENABLE('JOB_LOAD_STK_INDX_DAILY');
    
    DBMS_OUTPUT.PUT_LINE('Job创建成功!');
END;
/

1.3 查看和管理Scheduler Job

复制代码
-- 查看所有Job
SELECT job_name, enabled, state, last_start_date, next_run_date, run_count, failure_count
FROM USER_SCHEDULER_JOBS
WHERE job_name LIKE '%STK_INDX%';

-- 查看Job运行历史
SELECT job_name, log_date, status, error#
FROM USER_SCHEDULER_JOB_LOG
WHERE job_name = 'JOB_LOAD_STK_INDX_DAILY'
ORDER BY log_date DESC;

-- 查看Job运行详情
SELECT log_id, log_date, status, error#, req_start_date, actual_start_date, run_duration
FROM USER_SCHEDULER_JOB_RUN_DETAILS
WHERE job_name = 'JOB_LOAD_STK_INDX_DAILY'
ORDER BY log_date DESC;

-- 立即执行Job
BEGIN
    DBMS_SCHEDULER.RUN_JOB('JOB_LOAD_STK_INDX_DAILY');
END;
/

-- 停止Job
BEGIN
    DBMS_SCHEDULER.STOP_JOB('JOB_LOAD_STK_INDX_DAILY');
END;
/

-- 禁用Job
BEGIN
    DBMS_SCHEDULER.DISABLE('JOB_LOAD_STK_INDX_DAILY');
END;
/

-- 删除Job
BEGIN
    DBMS_SCHEDULER.DROP_JOB('JOB_LOAD_STK_INDX_DAILY');
END;
/

1.4 创建带监控和日志的增强版Job

复制代码
-- 首先创建日志表
CREATE TABLE STK_INDX_JOB_LOG (
    LOG_ID         NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    JOB_NAME       VARCHAR2(100) NOT NULL,
    START_TIME     TIMESTAMP DEFAULT SYSTIMESTAMP NOT NULL,
    END_TIME       TIMESTAMP,
    STATUS         VARCHAR2(20) DEFAULT 'RUNNING' CHECK (STATUS IN ('RUNNING', 'SUCCESS', 'ERROR', 'WARNING')),
    ROW_COUNT      NUMBER,
    ERROR_MSG      VARCHAR2(4000),
    DURATION_SEC   NUMBER,
    CREATED_DATE   DATE DEFAULT SYSDATE
);

-- 创建增强版存储过程
CREATE OR REPLACE PROCEDURE PROC_LOAD_STK_INDX_WITH_LOG
AS
    v_job_name VARCHAR2(100) := 'JOB_LOAD_STK_INDX_DAILY';
    v_log_id NUMBER;
    v_row_count NUMBER;
    v_start_time TIMESTAMP;
    v_end_time TIMESTAMP;
BEGIN
    v_start_time := SYSTIMESTAMP;
    
    -- 插入开始日志
    INSERT INTO STK_INDX_JOB_LOG (JOB_NAME, START_TIME, STATUS)
    VALUES (v_job_name, v_start_time, 'RUNNING')
    RETURNING LOG_ID INTO v_log_id;
    
    DBMS_OUTPUT.PUT_LINE('Job [' || v_job_name || '] 开始执行,日志ID: ' || v_log_id);
    
    -- 执行核心装载逻辑
    PROC_LOAD_STK_MKT_QUOT_INDX;
    
    -- 获取行数(需要修改原存储过程使其返回行数)
    v_row_count := 0;  -- 这里需要从存储过程获取实际行数
    
    v_end_time := SYSTIMESTAMP;
    
    -- 更新成功日志
    UPDATE STK_INDX_JOB_LOG
    SET END_TIME = v_end_time,
        STATUS = 'SUCCESS',
        ROW_COUNT = v_row_count,
        DURATION_SEC = ROUND(EXTRACT(SECOND FROM (v_end_time - v_start_time)) + 
                            EXTRACT(MINUTE FROM (v_end_time - v_start_time)) * 60, 3)
    WHERE LOG_ID = v_log_id;
    
    COMMIT;
    
    DBMS_OUTPUT.PUT_LINE('Job执行成功,耗时: ' || 
        ROUND(EXTRACT(SECOND FROM (v_end_time - v_start_time)) + 
              EXTRACT(MINUTE FROM (v_end_time - v_start_time)) * 60, 3) || '秒');
    
EXCEPTION
    WHEN OTHERS THEN
        v_end_time := SYSTIMESTAMP;
        
        -- 更新错误日志
        UPDATE STK_INDX_JOB_LOG
        SET END_TIME = v_end_time,
            STATUS = 'ERROR',
            ERROR_MSG = SQLERRM,
            DURATION_SEC = ROUND(EXTRACT(SECOND FROM (v_end_time - v_start_time)) + 
                                EXTRACT(MINUTE FROM (v_end_time - v_start_time)) * 60, 3)
        WHERE LOG_ID = v_log_id;
        
        COMMIT;
        
        DBMS_OUTPUT.PUT_LINE('Job执行失败: ' || SQLERRM);
        RAISE;
END;
/

-- 创建调度Job
BEGIN
    DBMS_SCHEDULER.CREATE_JOB (
        job_name        => 'JOB_STK_INDX_ENHANCED',
        job_type        => 'PLSQL_BLOCK',
        job_action      => 'BEGIN PROC_LOAD_STK_INDX_WITH_LOG; END;',
        start_date      => SYSTIMESTAMP,
        repeat_interval => 'FREQ=DAILY; BYHOUR=2; BYMINUTE=30',  -- 每天凌晨2:30
        enabled         => TRUE,
        comments        => '带日志监控的股票指标装载作业'
    );
END;
/

方案2:使用传统的 DBMS_JOB(兼容旧版本)

复制代码
-- 1. 提交Job(立即执行,每天重复)
DECLARE
    v_jobno NUMBER;
BEGIN
    DBMS_JOB.SUBMIT(
        job        => v_jobno,
        what       => 'BEGIN PROC_LOAD_STK_MKT_QUOT_INDX; END;',
        next_date  => SYSDATE,  -- 立即执行
        interval   => 'SYSDATE + 1',  -- 每天执行
        no_parse   => FALSE
    );
    
    COMMIT;
    DBMS_OUTPUT.PUT_LINE('Job提交成功,Job编号: ' || v_jobno);
END;
/

-- 2. 查看Job
SELECT job, what, last_date, next_date, interval, broken, failures
FROM USER_JOBS;

-- 3. 修改Job执行时间
BEGIN
    DBMS_JOB.INTERVAL(123, 'SYSDATE + 1/24');  -- 改为每小时执行
    COMMIT;
END;
/

-- 4. 删除Job
BEGIN
    DBMS_JOB.REMOVE(123);
    COMMIT;
END;
/

方案3:使用操作系统的定时任务 + SQL脚本

3.1 创建执行脚本

Windows (stk_indx_job.bat):

复制代码
@echo off
set ORACLE_HOME=C:\app\oracle\product\19.0.0\dbhome_1
set PATH=%ORACLE_HOME%\bin;%PATH%
set ORACLE_SID=ORCL
set USERNAME=your_user
set PASSWORD=your_password

echo 开始执行股票指标装载作业: %date% %time%
sqlplus -S %USERNAME%/%PASSWORD%@%ORACLE_SID% @C:\scripts\stk_indx_job.sql
echo 作业执行完成: %date% %time%

stk_indx_job.sql:

复制代码
SET SERVEROUTPUT ON
SET FEEDBACK OFF
SET VERIFY OFF
SET ECHO OFF

BEGIN
    DBMS_OUTPUT.PUT_LINE('开始执行股票指标装载: ' || TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'));
    PROC_LOAD_STK_MKT_QUOT_INDX;
    DBMS_OUTPUT.PUT_LINE('执行完成: ' || TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'));
EXCEPTION
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('执行失败: ' || SQLERRM);
        RAISE;
END;
/

EXIT;

Linux (stk_indx_job.sh):

复制代码
#!/bin/bash
export ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1
export PATH=$ORACLE_HOME/bin:$PATH
export ORACLE_SID=ORCL

echo "开始执行股票指标装载作业: $(date)"
sqlplus -S your_user/your_password@ORCL <<EOF
SET SERVEROUTPUT ON
SET FEEDBACK OFF
BEGIN
    DBMS_OUTPUT.PUT_LINE('开始执行: ' || TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'));
    PROC_LOAD_STK_MKT_QUOT_INDX;
    DBMS_OUTPUT.PUT_LINE('执行完成: ' || TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'));
END;
/
EXIT;
EOF
echo "作业执行完成: $(date)"

3.2 配置操作系统的定时任务

Windows 任务计划程序

  1. 打开"任务计划程序"
  2. 创建基本任务
  3. 设置触发器(每天凌晨1点)
  4. 操作:启动程序 C:\scripts\stk_indx_job.bat
  5. 设置条件和工作目录

Linux Cron

复制代码
# 编辑crontab
crontab -e

# 添加定时任务(每天凌晨1点执行)
0 1 * * * /home/oracle/scripts/stk_indx_job.sh >> /home/oracle/scripts/job.log 2>&1

# 查看crontab
crontab -l

方案4:结合Kettle和数据库调度

4.1 在Kettle中创建作业调度

在Kettle中创建作业,包含:

  1. 开始步骤
  2. 执行SQL脚本(调用存储过程)
  3. 成功/失败处理
  4. 邮件通知(可选)

然后使用Kettle的作业调度功能或操作系统的定时任务调用Kettle作业。

4.2 使用数据库Job调用Kettle转换

复制代码
-- 创建调用Kettle的存储过程
CREATE OR REPLACE PROCEDURE PROC_RUN_KETTLE_JOB
AS
    v_job_path VARCHAR2(500) := '/home/kettle/jobs/load_stk_indx.kjb';
BEGIN
    -- 通过Java或外部程序调用Kettle
    DBMS_OUTPUT.PUT_LINE('调用Kettle作业: ' || v_job_path);
    
    -- 示例:通过Java调用
    -- 实际实现需要编写Java存储过程或使用外部作业调度器
END;
/

-- 创建调度Job
BEGIN
    DBMS_SCHEDULER.CREATE_JOB(
        job_name        => 'JOB_RUN_KETTLE_STK_INDX',
        job_type        => 'EXECUTABLE',
        job_action      => '/home/kettle/kitchen.sh',
        number_of_arguments => 5,
        enabled         => FALSE
    );
    
    DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE('JOB_RUN_KETTLE_STK_INDX', 1, '-file=/home/kettle/jobs/load_stk_indx.kjb');
    DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE('JOB_RUN_KETTLE_STK_INDX', 2, '-level=Basic');
    DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE('JOB_RUN_KETTLE_STK_INDX', 3, '-logfile=/home/kettle/logs/stk_indx.log');
    
    DBMS_SCHEDULER.ENABLE('JOB_RUN_KETTLE_STK_INDX');
END;
/

方案比较

方案 优点 缺点 适用场景
DBMS_SCHEDULER 功能强大,支持复杂调度,有完善监控 Oracle 10g+ 可用 生产环境推荐
DBMS_JOB 兼容性好,简单易用 功能较简单,旧版本可能不再更新 旧版本Oracle或简单任务
操作系统Cron 跨数据库通用,调度灵活 需要数据库连接信息,安全性考虑 多数据库环境或混合调度
Kettle调度 可视化配置,有作业流控制 依赖Kettle服务,增加系统复杂度 已有Kettle调度体系

推荐的生产环境方案

综合方案:DBMS_SCHEDULER + 监控 + 报警

复制代码
-- 1. 创建增强版存储过程(带日志和异常处理)
CREATE OR REPLACE PROCEDURE PROC_LOAD_STK_INDX_PROD
AS
    PRAGMA AUTONOMOUS_TRANSACTION;
    v_job_name VARCHAR2(100) := 'STK_INDX_LOAD';
    v_log_id NUMBER;
    v_start_time TIMESTAMP := SYSTIMESTAMP;
    v_row_count NUMBER;
BEGIN
    -- 记录开始
    INSERT INTO JOB_EXECUTION_LOG (
        JOB_NAME, START_TIME, STATUS, PARAMETERS
    ) VALUES (
        v_job_name, v_start_time, 'STARTED',
        'FULL_LOAD=Y'
    ) RETURNING LOG_ID INTO v_log_id;
    
    COMMIT;
    
    -- 执行核心业务逻辑
    PROC_LOAD_STK_MKT_QUOT_INDX;
    
    -- 获取处理行数(假设原存储过程有输出参数)
    v_row_count := SQL%ROWCOUNT;
    
    -- 记录成功
    UPDATE JOB_EXECUTION_LOG
    SET END_TIME = SYSTIMESTAMP,
        STATUS = 'SUCCESS',
        ROW_COUNT = v_row_count,
        MESSAGE = '处理完成: ' || v_row_count || ' 行',
        DURATION = (SYSTIMESTAMP - v_start_time) * 86400
    WHERE LOG_ID = v_log_id;
    
    COMMIT;
    
    -- 可选的报警通知(调用邮件存储过程)
    -- SEND_JOB_SUCCESS_ALERT(v_job_name, v_row_count, v_start_time, SYSTIMESTAMP);
    
EXCEPTION
    WHEN OTHERS THEN
        -- 记录失败
        UPDATE JOB_EXECUTION_LOG
        SET END_TIME = SYSTIMESTAMP,
            STATUS = 'FAILED',
            ERROR_MSG = SQLERRM,
            ERROR_CODE = SQLCODE,
            DURATION = (SYSTIMESTAMP - v_start_time) * 86400
        WHERE LOG_ID = v_log_id;
        
        COMMIT;
        
        -- 发送失败报警
        -- SEND_JOB_FAILURE_ALERT(v_job_name, SQLCODE, SQLERRM);
        
        RAISE;
END;
/

-- 2. 创建调度Job
BEGIN
    DBMS_SCHEDULER.CREATE_JOB(
        job_name        => 'JOB_STK_INDX_PROD',
        job_type        => 'PLSQL_BLOCK',
        job_action      => 'BEGIN PROC_LOAD_STK_INDX_PROD; END;',
        start_date      => TRUNC(SYSDATE) + INTERVAL '2' HOUR,  -- 凌晨2点
        repeat_interval => 'FREQ=DAILY; BYHOUR=2',
        enabled         => TRUE,
        comments        => '生产环境股票指标装载作业'
    );
END;
/

-- 3. 创建监控视图
CREATE OR REPLACE VIEW V_JOB_STK_INDX_MONITOR AS
SELECT 
    JOB_NAME,
    LAST_START_DATE,
    NEXT_RUN_DATE,
    STATE,
    RUN_COUNT,
    FAILURE_COUNT,
    (SELECT MAX(START_TIME) FROM JOB_EXECUTION_LOG WHERE JOB_NAME = J.JOB_NAME) AS LAST_EXEC_TIME,
    (SELECT STATUS FROM JOB_EXECUTION_LOG WHERE JOB_NAME = J.JOB_NAME AND START_TIME = 
        (SELECT MAX(START_TIME) FROM JOB_EXECUTION_LOG WHERE JOB_NAME = J.JOB_NAME)) AS LAST_STATUS
FROM USER_SCHEDULER_JOBS J
WHERE JOB_NAME LIKE '%STK_INDX%';

最佳实践建议

  1. 使用DBMS_SCHEDULER:功能最全,Oracle官方推荐
  2. 添加完整日志:记录开始、结束、行数、耗时
  3. 设置超时控制:避免长时间运行
  4. 配置失败重试:可设置重试次数
  5. 添加监控报警:邮件或短信通知
  6. 定期清理日志:避免日志表过大
  7. 测试环境验证:先在测试环境验证调度逻辑
  8. 文档记录:记录作业目的、时间、负责人

推荐的最小化配置示例

复制代码
-- 最简单的生产可用方案
BEGIN
    DBMS_SCHEDULER.CREATE_JOB(
        job_name        => 'JOB_LOAD_STK_INDX_DAILY',
        job_type        => 'PLSQL_BLOCK',
        job_action      => 'BEGIN PROC_LOAD_STK_MKT_QUOT_INDX; END;',
        start_date      => TRUNC(SYSDATE) + 1 + 1/24,  -- 明天凌晨1点
        repeat_interval => 'FREQ=DAILY; INTERVAL=1',
        enabled         => TRUE
    );
END;
/

这样配置后,存储过程就会在每天凌晨1点自动执行,实现完全自动化的数据装载。

相关推荐
熬夜的咕噜猫2 小时前
数据库常用SQL命令
数据库·oracle
网硕互联的小客服2 小时前
CentOS 7 系统开通后如何修改数据盘挂载目录?
运维·服务器·网络·安全·自动化
William Dawson2 小时前
【实战分享】DTU设备高并发数据接入全流程(Redis + RabbitMQ + 数据库)
数据库·redis·rabbitmq
wregjru2 小时前
【MySQL】5. 数据更新与查询详解
java·数据库·mysql
牛奶咖啡132 小时前
DevOps自动化运维实践_ansible-playbook的使用
运维·自动化·ansible·devops·playbook·playbook模块及其示例
洛菡夕2 小时前
PG数据库日常应用
数据库
淼淼爱喝水2 小时前
Ansible Ad-Hoc 命令基础实战(Linux 系统)
linux·服务器·数据库
CodeMartain5 小时前
Redis为什么快?
数据库·redis·缓存
Anastasiozzzz7 小时前
深入研究RAG: 在线阶段-查询&问答
数据库·人工智能·ai·embedding