进阶-存储对象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操作

  • 业务逻辑变化频繁

  • 需要跨数据库移植

相关推荐
杨杨杨大侠2 小时前
深入理解 LLVM:从编译器原理到 JIT 实战
java·jvm·编译器
码农胖虎-java2 小时前
【AI】向量数据库选型实战:pgvector vs Milvus vs Qdrant
数据库·milvus·pg
Mr -老鬼2 小时前
Rust 知识图谱 -进阶部分
开发语言·后端·rust
LawrenceLan2 小时前
Flutter 零基础入门(十三):late 关键字与延迟初始化
开发语言·前端·flutter·dart
深耕AI2 小时前
【wordpress系列教程】03 网站页面的编辑
开发语言·前端
Insist7532 小时前
KingbaseES 集群运维案例之 --- 集群架构拆分为单实例操作
网络·数据库·oracle
m0_598177232 小时前
MySQL项目开发 (2)
数据库·mysql
qq_336313932 小时前
java基础-IO流(随机点名器)
java·开发语言·python