进阶-存储对象2-存储过程上

一、MySQL进阶

1. 存储过程

1.1 传统SQL的局限性

在早期的数据库应用开发中,我们面临这样的困境:

sql 复制代码
-- 一个典型的电商订单处理流程需要执行多个独立操作
START TRANSACTION;

-- 1. 检查库存
SELECT quantity FROM products WHERE id = 1001;

-- 2. 插入订单记录
INSERT INTO orders (user_id, total_amount) VALUES (123, 299.00);

-- 3. 更新库存
UPDATE products SET quantity = quantity - 1 WHERE id = 1001;

-- 4. 记录操作日志
INSERT INTO operation_logs (user_id, action) VALUES (123, 'place_order');

COMMIT;

问题暴露

  • 📍 网络开销大:每次操作都要进行网络往返

  • 📍 代码重复:同样的逻辑在不同地方重复编写

  • 📍 维护困难:业务逻辑分散在应用代码各处

  • 📍 安全性差:SQL注入风险高

1.2 存储过程的诞生

就像从"手工作坊"进化到"自动化工厂",存储过程(Stored Procedure)应运而生,它将一系列SQL语句封装在数据库服务器端,提供了一种更高效、更安全的数据库操作方式。

存储过程是一组为了完成特定功能的SQL语句集合,经编译后存储在数据库中,用户通过指定存储过程的名字并给出参数来调用执行。

形象的比喻:

传统SQL 存储过程
手工组装家具(按说明书一步步来) 买成品家具(一键安装)
每次都要告诉厨师怎么做菜 告诉厨师"我要红烧肉"
手动开车(换挡、踩油门) 自动驾驶
sql 复制代码
应用程序层
    ↓ 调用(传入参数)
数据库层 → 存储过程执行引擎
    ↓ 编译优化
    ↓ 执行SQL语句
    ↓ 返回结果
应用程序层 ← 接收返回数据

1.3 基本语法框架

在命令行中:

sql 复制代码
-- 基础语法模板
DELIMITER $$  -- 更改分隔符,避免与过程体中的分号冲突

CREATE PROCEDURE procedure_name([参数列表])
BEGIN
    -- 过程体:SQL语句集合
END $$

DELIMITER ;  -- 恢复分隔符
sql 复制代码
-- 1. 创建存储过程
DELIMITER $$
CREATE PROCEDURE ProcessOrder()
BEGIN
    -- 过程逻辑
END $$
DELIMITER ;

-- 2. 查看存储过程
SHOW PROCEDURE STATUS WHERE Db = 'your_database';
SHOW CREATE PROCEDURE ProcessOrder;

-- 3. 调用存储过程
CALL ProcessOrder();

-- 4. 修改存储过程(必须先删除再创建)
DROP PROCEDURE IF EXISTS ProcessOrder;
-- 重新CREATE

-- 5. 删除存储过程
DROP PROCEDURE ProcessOrder;

1.4 三种变量的使用艺术

系统变量

系统变量是MySQL服务器维护的配置参数,分为全局会话级别。不设置的话默认是会话变量session

全局变量:在所有的会话窗口中都有效!

会话变量:只在创建的会话窗口中有效!

sql 复制代码
-- 查看所有系统变量
SHOW VARIABLES;

-- 查看特定系统变量
SHOW VARIABLES LIKE 'autocommit';
SHOW VARIABLES LIKE 'wait_timeout';

-- 全局变量(影响所有新连接)
-- 查看全局变量
SELECT @@GLOBAL.autocommit;
SHOW GLOBAL VARIABLES LIKE 'autocommit';

-- 设置全局变量(需要SUPER权限)
SET GLOBAL wait_timeout = 300;
SET @@GLOBAL.wait_timeout = 300;

-- 会话变量(只影响当前连接)
-- 查看会话变量
SELECT @@SESSION.autocommit;
SHOW SESSION VARIABLES LIKE 'autocommit';

-- 设置会话变量
SET SESSION autocommit = 0;
SET @@SESSION.autocommit = 0;
SET autocommit = 0;  -- 默认是SESSION

-- 在存储过程中使用系统变量示例
DELIMITER $$
CREATE PROCEDURE CheckConnectionSettings()
BEGIN
    DECLARE autoCommitVal BOOLEAN;
    DECLARE timeoutVal INT;
    
    -- 获取当前会话的设置
    SET autoCommitVal = @@SESSION.autocommit;
    SET timeoutVal = @@SESSION.wait_timeout;
    
    SELECT 
        CONCAT('AutoCommit: ', autoCommitVal) AS Setting1,
        CONCAT('Wait Timeout: ', timeoutVal, ' seconds') AS Setting2;
END $$
DELIMITER ;

CALL CheckConnectionSettings();
用户定义变量

用户变量以@开头,在当前会话中有效,可以跨多个SQL语句使用。

sql 复制代码
-- 定义用户变量(三种方式)
SET @userCount = 0;
SELECT @totalPrice := 100.50;  -- 使用 := 赋值
SELECT COUNT(*) INTO @userCount FROM users;

-- 使用用户变量
SELECT @userCount AS TotalUsers;

-- 在多个语句间共享数据
SET @discountRate = 0.1;  -- 10%折扣

SELECT 
    product_name,
    price,
    price * @discountRate AS discount_amount,
    price * (1 - @discountRate) AS final_price
FROM products
WHERE category = 'Electronics';

-- 用户变量在存储过程中的应用
DELIMITER $$
CREATE PROCEDURE CalculateOrderSummary(IN orderId INT)
BEGIN
    -- 使用用户变量存储中间结果
    SELECT SUM(quantity * price) INTO @subtotal
    FROM order_items WHERE order_id = orderId;
    
    -- 计算税费(假设税率8%)
    SET @tax = @subtotal * 0.08;
    
    -- 计算总计
    SET @total = @subtotal + @tax;
    
    -- 返回所有计算结果
    SELECT 
        @subtotal AS subtotal,
        @tax AS tax,
        @total AS total_amount;
END $$
DELIMITER ;

CALL CalculateOrderSummary(1001);

注意事项

  • 用户变量在整个会话期间都有效

  • 多个存储过程可以共享用户变量

  • 用户变量名不区分大小写(MySQL中)

局部变量

局部变量在存储过程内部声明和使用,生命周期仅限于BEGIN-END块。

三种变量的对比分析
特性 系统变量 用户定义变量 局部变量
前缀 @@ @
作用域 全局/会话 当前会话 BEGIN-END块
生命周期 重启前/会话结束 会话结束 块结束
声明方式 MySQL内置 SET或SELECT DECLARE
数据类型 系统决定 动态类型 必须声明类型
典型用途 服务器配置 跨语句数据传递 过程内部计算

1.5 为什么选择存储过程?

性能优势
  • 减少网络传输:一次调用代替多次SQL请求

  • 预编译优化:SQL语句只需编译一次

  • 减少解析时间:避免重复解析相同的SQL

安全优势
  • 权限控制:可限制直接表访问,只通过存储过程操作

  • SQL注入防护:参数化调用减少注入风险

  • 审计跟踪:所有操作通过存储过程记录日志

维护优势
  • 业务逻辑集中:修改只需在数据库端进行

  • 版本控制:存储过程可统一管理版本

  • 减少客户端代码:简化应用程序开发

何时使用存储过程?

适合场景

  • 复杂的业务逻辑处理

  • 批量数据操作

  • 需要事务完整性的操作

  • 高频执行的查询逻辑

不适合场景

  • 简单的CRUD操作

  • 业务逻辑变化频繁

  • 需要跨数据库移植

相关推荐
lzhdim11 小时前
C#开发的提示显示例子 - 开源研究系列文章
开发语言·c#
物联网软硬件开发-轨物科技11 小时前
【轨物方案】告别“盲维”时代:如何不动一根电线,帮老旧电站找回消失的 5% 收益?
服务器·网络·数据库
有代理ip11 小时前
成功请求的密码:HTTP 2 开头响应码深度解析
java·大数据·python·算法·php
呱呱巨基11 小时前
c语言 文件操作
c语言·开发语言·c++·笔记·学习
司沐_Simuoss11 小时前
Text to SQL系统的千层套路~
数据库·人工智能·sql·语言模型·系统架构
xb113211 小时前
C# 定时器和后台任务
开发语言·c#
ptc学习者11 小时前
验证mysql RR隔离水平,并未完全实现防止幻读的实验
数据库
CoderCodingNo11 小时前
【GESP】C++五级练习题 luogu-P1031 [NOIP 2002 提高组] 均分纸牌
开发语言·c++·算法
小天源11 小时前
银河麒麟 V10(x86_64)离线安装 MySQL 8.0
android·mysql·adb·麒麟v10