存储过程,存储函数

数据库中存储过程(Stored Procedure)存储函数(Stored Function) 的核心概念、区别以及实际用法


一、核心概念(以MySQL为例)

1. 存储过程(Stored Procedure)

可以把它理解为数据库中的"脚本/子程序",是一组为了完成特定功能的SQL语句集合,经编译后存储在数据库中。你可以通过调用它来执行这一系列SQL操作,支持传入参数、输出参数,也可以无参数。

  • 核心特点:执行一个"操作序列",重点在"过程/动作",返回值不是必需的(可以通过输出参数返回结果)。

2. 存储函数(Stored Function)

可以把它理解为数据库中的"自定义函数" ,也是一组SQL语句集合,但必须有返回值 (且只能返回一个值),可以像调用系统函数(如SUM()CONCAT())一样调用它。

  • 核心特点:计算并返回一个"结果值",重点在"计算/返回值",必须有返回值,且返回值类型需明确声明。

二、核心区别(新手必记)

特性 存储过程 存储函数
返回值 可选(可通过OUT参数返回多个值) 必须有(且仅返回一个值)
调用方式 使用CALL语句调用 可作为表达式一部分调用(如SELECT 函数名()
适用场景 执行复杂业务逻辑(增删改查组合) 计算并返回单个值(如自定义计算、数据转换)
能否包含事务 可以(如BEGIN/COMMIT/ROLLBACK) 不建议(通常用于简单计算,不处理事务)
参数类型 IN/OUT/INOUT(输入/输出/输入输出) 仅IN类型(输入参数)

三、实战示例(MySQL)

1. 存储过程示例(查询指定用户的订单数,通过OUT参数返回)

sql 复制代码
-- 创建存储过程:查询指定用户ID的订单总数
DELIMITER //  -- 临时修改语句结束符为//(避免与SQL中的;冲突)
CREATE PROCEDURE GetUserOrderCount(
    IN user_id INT,          -- 输入参数:用户ID
    OUT order_count INT      -- 输出参数:订单总数
)
BEGIN
    -- 核心逻辑:统计订单数并赋值给输出参数
    SELECT COUNT(*) INTO order_count 
    FROM orders 
    WHERE user_id = user_id;
END //
DELIMITER ;  -- 恢复语句结束符为;

-- 调用存储过程
SET @count = 0;  -- 定义变量接收输出结果
CALL GetUserOrderCount(1001, @count);  -- 调用:查询用户1001的订单数
SELECT @count;  -- 查看结果(如返回5,表示该用户有5个订单)

2. 存储函数示例(计算两个数的乘积)

sql 复制代码
-- 创建存储函数:计算两个数的乘积
DELIMITER //
CREATE FUNCTION CalculateProduct(
    num1 INT,  -- 输入参数1
    num2 INT   -- 输入参数2
) RETURNS INT  -- 声明返回值类型为INT
DETERMINISTIC  -- 确定性函数(相同输入必返回相同输出)
BEGIN
    RETURN num1 * num2;  -- 必须有RETURN语句返回值
END //
DELIMITER ;

-- 调用存储函数(像系统函数一样使用)
SELECT CalculateProduct(5, 8);  -- 输出:40
SELECT id, name, CalculateProduct(price, quantity) AS total FROM products;  -- 结合表查询

3. 存储过程的复杂示例(带事务的新增用户+订单)

sql 复制代码
-- 创建存储过程:新增用户并创建初始订单(包含事务)
DELIMITER //
CREATE PROCEDURE AddUserWithOrder(
    IN username VARCHAR(50),
    IN order_amount DECIMAL(10,2),
    OUT result VARCHAR(100)  -- 返回执行结果(成功/失败)
)
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION  -- 异常处理:出错则回滚
    BEGIN
        ROLLBACK;
        SET result = '失败:' || SQLSTATE || ' ' || SQLERRM;
    END;

    START TRANSACTION;  -- 开启事务

    -- 1. 新增用户
    INSERT INTO users (name) VALUES (username);
    -- 获取刚插入的用户ID
    SET @new_user_id = LAST_INSERT_ID();
    -- 2. 为该用户创建初始订单
    INSERT INTO orders (user_id, amount) VALUES (@new_user_id, order_amount);

    COMMIT;  -- 提交事务
    SET result = CONCAT('成功:用户', @new_user_id, '创建并生成初始订单');
END //
DELIMITER ;

-- 调用
SET @res = '';
CALL AddUserWithOrder('张三', 99.9, @res);
SELECT @res;  -- 查看执行结果

四、使用注意事项(新手避坑)

  1. 性能:存储过程/函数编译后存储,首次调用慢,后续调用快,适合高频执行的逻辑;但过度使用会增加数据库负担,建议仅用于核心业务逻辑。
  2. 可维护性:存储过程/函数写在数据库中,调试、版本管理不如应用代码方便,需结合业务场景选择(如简单计算用函数,复杂事务用过程)。
  3. 权限 :创建需要CREATE ROUTINE权限,调用需要EXECUTE权限,需注意数据库权限管控。
  4. 移植性:不同数据库(MySQL、Oracle、SQL Server)的语法差异较大(如Oracle的存储过程语法与MySQL不同),迁移成本高。

总结

  1. 存储过程 :侧重"执行动作/业务逻辑",支持输入/输出参数、事务,用CALL调用,无强制返回值,适合复杂的增删改查组合操作。
  2. 存储函数:侧重"计算并返回值",仅支持输入参数,必须返回单个值,可像系统函数一样调用,适合简单的数值/数据转换计算。
  3. 核心选择原则:需要返回单个值用函数 ,需要执行复杂逻辑(如事务、多步操作)用过程

你想了解数据库中游标(Cursor) 的核心概念、使用场景和具体用法,我会结合新手容易理解的例子(以MySQL为例),帮你搞清楚游标是什么、为什么用、怎么用,以及使用时的注意事项。


相关推荐
IvorySQL3 小时前
PG 日报|新增 VACUUM 全维度统计信息补丁迭代
数据库·人工智能·postgresql·ivorysql
白露与泡影6 小时前
Redis 缓存实战:雪崩、穿透、击穿一次讲透
数据库·redis·缓存
赵渝强老师7 小时前
【赵渝强老师】使用Oracle可传输的表空间迁移数据
数据库·oracle
IpdataCloud8 小时前
跨区服玩家延迟高怎么办?用IP离线库自动分配到最近服务器
数据库·tcp/ip·github·php·ip
wuqingshun3141599 小时前
MYSQL默认的事务隔离级别是什么?为什么选择这个级别?
数据库·mysql
空中湖9 小时前
电池简史(五):极限之梦与终章——锂硫、锂空气、量子电池,以及你该知道的电池投资与生活指南
数据库·程序人生
蓝胖子酱11 小时前
在 Electron 中用 electron-store 实现加密存储与自定义格式文件
前端·javascript·数据库
snow@li11 小时前
Redis:数据库语法速查表 / 全景梳理
数据库·redis·缓存
Devlive 开源社区11 小时前
Nebula 1.6.0 发布:跨区域桶自动路由、秒传、文本/PDF 预览一次到位
数据库·sql
这个DBA有点耶12 小时前
导个数据中文全变问号?字符集这事儿真没那么简单
数据库·mysql·代码规范