Oracle存储过程怎么写

Oracle 存储过程(Stored Procedure)是存储在数据库中的一组预编译 SQL 和 PL/SQL 代码块,用于封装复杂的业务逻辑。

以下是完整的编写指南,包含基本结构参数类型常用逻辑 以及完整示例

1. 基本语法结构

一个标准的存储过程由三部分组成:声明部分执行部分异常处理部分

sql 复制代码
CREATE OR REPLACE PROCEDURE 过程名 (
    参数1 IN 数据类型,      -- 输入参数
    参数2 OUT 数据类型,     -- 输出参数
    参数3 IN OUT 数据类型   -- 输入输出参数
) IS
    -- 【声明部分】定义局部变量、游标、常量等
    v_variable_name  VARCHAR2(100);
    v_count          NUMBER := 0;
BEGIN
    -- 【执行部分】编写具体的 SQL 和业务逻辑
    SELECT count(*) INTO v_count FROM 表名 WHERE 条件;
    
    IF v_count > 0 THEN
        -- 业务逻辑...
        参数2 := '成功'; -- 给输出参数赋值
    ELSE
        参数2 := '失败';
    END IF;

EXCEPTION
    -- 【异常处理部分】捕获并处理错误
    WHEN NO_DATA_FOUND THEN
        DBMS_OUTPUT.PUT_LINE('未找到数据');
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('发生错误: ' || SQLERRM);
END 过程名;

2. 三种参数模式

模式 关键字 说明 使用场景
输入 IN 默认模式。调用时传入值,过程内只读。 查询条件、配置值。
输出 OUT 调用时需传入变量接收返回值,过程内可赋值。 返回统计结果、状态码、生成的主键ID。
输入输出 IN OUT 传入初始值,过程处理后修改该值并返回。 累加计算、字符串拼接处理。

3. 实战示例:用户信息处理

假设有一个表 USERS (ID, NAME, AGE, CITY)。我们要写一个存储过程:

  1. 根据 ID 查询用户。
  2. 如果用户年龄大于 18 岁且城市包含"市",则去掉城市后缀(类似你之前的 JS 逻辑)。
  3. 返回处理后的城市和状态消息。
sql 复制代码
CREATE OR REPLACE PROCEDURE PROC_UPDATE_USER_CITY (
    p_user_id   IN  NUMBER,           -- 输入:用户ID
    p_city_out  OUT VARCHAR2,         -- 输出:处理后的城市
    p_msg       OUT VARCHAR2          -- 输出:执行消息
) IS
    v_age       NUMBER;
    v_city      VARCHAR2(100);
    v_name      VARCHAR2(100);
BEGIN
    -- 1. 查询数据
    SELECT age, city, name 
    INTO v_age, v_city, v_name
    FROM USERS 
    WHERE id = p_user_id;

    -- 2. 业务逻辑判断
    IF v_age > 18 THEN
        -- 模拟去除城市后缀逻辑
        IF INSTR(v_city, '市') > 0 AND v_city NOT LIKE '%自治州%' THEN
            v_city := REPLACE(v_city, '市', '');
        END IF;
        
        p_msg := '用户 ' || v_name || ' 处理成功,新城市为:' || v_city;
    ELSE
        p_msg := '用户未成年,不处理城市信息。';
    END IF;

    -- 3. 赋值给输出参数
    p_city_out := v_city;

EXCEPTION
    WHEN NO_DATA_FOUND THEN
        p_msg := '错误:未找到 ID 为 ' || p_user_id || ' 的用户';
        p_city_out := NULL;
    WHEN OTHERS THEN
        p_msg := '系统异常:' || SQLERRM;
        p_city_out := NULL;
        -- 可选:记录日志到日志表
        -- INSERT INTO error_log VALUES (SYSDATE, SQLERRM);
END PROC_UPDATE_USER_CITY;

4. 如何调用存储过程

方法 A:在 SQL Developer / PLSQL Developer 中测试
sql 复制代码
DECLARE
    v_result_city VARCHAR2(100);
    v_message     VARCHAR2(200);
BEGIN
    -- 调用过程
    PROC_UPDATE_USER_CITY(
        p_user_id   => 1001,          -- 传入具体值
        p_city_out  => v_result_city, -- 传入变量接收
        p_msg       => v_message
    );
    
    -- 打印结果
    DBMS_OUTPUT.PUT_LINE(v_message);
    DBMS_OUTPUT.PUT_LINE('处理后城市:' || v_result_city);
END;

注意:使用前需开启输出显示 SET SERVEROUTPUT ON;

方法 B:在 Java / Python / Kettle 中调用
  • Java (JDBC): 使用 CallableStatement
  • Kettle: 使用 "Call DB Procedure" 组件,选择刚才创建的过程名,映射输入输出参数即可。

5. 常用技巧与注意事项

  1. CREATE OR REPLACE :

    • 始终加上 OR REPLACE,这样如果过程已存在,会直接覆盖更新,而不会报错。
  2. 变量赋值 :

    • 使用 := 进行赋值(如 v_count := 1;)。
    • 使用 SELECT ... INTO ... 将查询结果赋给变量。
  3. 动态 SQL :

    • 如果表名或列名需要动态变化,使用 EXECUTE IMMEDIATE
    sql 复制代码
    EXECUTE IMMEDIATE 'UPDATE ' || v_table_name || ' SET status=1 WHERE id=:1' USING p_id;
  4. 事务控制 :

    • 存储过程内部通常不写 COMMITROLLBACK,除非明确设计为独立事务。最好由调用者(外部程序)决定何时提交事务,以保持灵活性。
  5. 调试 :

    • 使用 DBMS_OUTPUT.PUT_LINE('变量值:' || v_var); 打印调试信息。

6. 常见错误排查

  • ORA-06550 / PLS-00201: 标识符必须声明(检查变量名拼写,或是否有权限访问该表)。
  • ORA-01403 : 未找到数据(SELECT INTO 没查到数据,需在 EXCEPTION 中捕获 NO_DATA_FOUND)。
  • ORA-01422 : 返回多于请求的行数(SELECT INTO 查出了多行,但变量只能存一行,需改用游标 CURSOR)。

如果你需要将之前 Kettle 中的 JavaScript 清洗逻辑迁移到 Oracle 存储过程中,我可以帮你直接转换那段代码。

相关推荐
笃行35010 小时前
金仓数据库数据安全双防线:静态存储加密与传输加密实战
数据库
笃行35011 小时前
金仓数据库物理备份实战:sys_rman 全流程演练与误覆盖抢救
数据库
笃行35011 小时前
金仓数据库逻辑备份实战:从全库导出到 Schema 替换的完整闭环
数据库
SelectDB1 天前
阶跃星辰基于 SelectDB 构建 PB 级 Agent 可观测平台
大数据·数据库·aigc
这个DBA有点耶1 天前
GROUP BY优化全解:如何写出既不丢数据又飞快的分组查询
数据库·mysql·架构
掉头发的王富贵2 天前
【StarRocks】极限十分钟入门StarRocks
数据库·sql·mysql
Nturmoils2 天前
WHERE 条件别凭习惯写,常用查询先跑一遍
数据库
Databend2 天前
在 AWS 中国峰会逛了一天,我在 Databend 展台看到了 Agent 数据基础设施的新思路
数据库·人工智能·agent
ClouGence4 天前
Oracle 数据同步为什么会出现数据不一致?长事务是常被忽略的原因
数据库·后端·oracle