MySQL: MySQL存储过程与函数详解

最后更新:2026-08-26

存储过程是预编译的 SQL 集合——一次编写,多次调用。

本课讲解存储过程和函数的创建与使用。

100%
graph TB
    A[CALL 存储过程] --> B[参数传递 IN/OUT/INOUT]
    B --> C[DECLARE 声明变量]
    C --> D{流程控制}
    D --> E[IF / CASE 条件]
    D --> F[WHILE / LOOP 循环]
    D --> G[游标 CURSOR 遍历]
    E --> H[执行SQL]
    F --> H
    G --> H
    H --> I[OUT参数返回]
    I --> J[结束]

1. 你将学到


2. 真实场景

(1) 痛点:复杂逻辑重复写

每月统计各部门工资总额,SQL 很长且重复执行。

(2) 存储过程的解法

SQL
CREATE PROCEDURE sp_dept_salary_stats()
BEGIN
    SELECT department, SUM(salary) AS total_salary, AVG(salary) AS avg_salary
    FROM employees
    GROUP BY department;
END;

-- 调用
CALL sp_dept_salary_stats();

3. 创建存储过程

▶ 示例:基本存储过程

SQL
-- 创建存储过程
DELIMITER //
CREATE PROCEDURE sp_get_user(IN user_id INT)
BEGIN
    SELECT * FROM users WHERE id = user_id;
END //
DELIMITER ;

-- 调用
CALL sp_get_user(1);
▶ 试一试

▶ 示例:带 OUT 参数

SQL
DELIMITER //
CREATE PROCEDURE sp_count_users(OUT total INT)
BEGIN
    SELECT COUNT(*) INTO total FROM users;
END //
DELIMITER ;

-- 调用
CALL sp_count_users(@total);
SELECT @total;
▶ 试一试

▶ 示例:带 INOUT 参数

SQL
DELIMITER //
CREATE PROCEDURE sp_double(INOUT num INT)
BEGIN
    SET num = num * 2;
END //
DELIMITER ;

-- 调用
SET @num = 5;
CALL sp_double(@num);
SELECT @num;  -- 10
▶ 试一试

4. 变量声明

▶ 示例:DECLARE 变量

SQL
DELIMITER //
CREATE PROCEDURE sp_variable_demo()
BEGIN
    DECLARE total INT DEFAULT 0;
    DECLARE avg_salary DECIMAL(10,2);
    DECLARE user_name VARCHAR(50);
    
    -- 赋值
    SELECT COUNT(*) INTO total FROM employees;
    SELECT AVG(salary) INTO avg_salary FROM employees;
    
    -- 使用变量
    SELECT total, avg_salary;
END //
DELIMITER ;
▶ 试一试

5. 流程控制

▶ 示例:IF 语句

SQL
DELIMITER //
CREATE PROCEDURE sp_check_salary(IN emp_id INT)
BEGIN
    DECLARE emp_salary DECIMAL(10,2);
    
    SELECT salary INTO emp_salary FROM employees WHERE id = emp_id;
    
    IF emp_salary > 10000 THEN
        SELECT 'High salary' AS level;
    ELSEIF emp_salary > 5000 THEN
        SELECT 'Medium salary' AS level;
    ELSE
        SELECT 'Low salary' AS level;
    END IF;
END //
DELIMITER ;
▶ 试一试

▶ 示例:CASE 语句

SQL
DELIMITER //
CREATE PROCEDURE sp_order_status(IN order_id INT)
BEGIN
    DECLARE order_status VARCHAR(20);
    
    SELECT status INTO order_status FROM orders WHERE id = order_id;
    
    CASE order_status
        WHEN 'pending' THEN SELECT 'Order is pending' AS message;
        WHEN 'paid' THEN SELECT 'Order is paid' AS message;
        WHEN 'shipped' THEN SELECT 'Order is shipped' AS message;
        ELSE SELECT 'Unknown status' AS message;
    END CASE;
END //
DELIMITER ;
▶ 试一试

▶ 示例:WHILE 循环

SQL
DELIMITER //
CREATE PROCEDURE sp_sum_1_to_n(IN n INT, OUT total INT)
BEGIN
    DECLARE i INT DEFAULT 1;
    SET total = 0;
    
    WHILE i <= n DO
        SET total = total + i;
        SET i = i + 1;
    END WHILE;
END //
DELIMITER ;

-- 调用
CALL sp_sum_1_to_n(100, @total);
SELECT @total;  -- 5050
▶ 试一试

6. 游标 CURSOR

▶ 示例:游标遍历

SQL
DELIMITER //
CREATE PROCEDURE sp_list_users()
BEGIN
    DECLARE done INT DEFAULT FALSE;
    DECLARE user_name VARCHAR(50);
    DECLARE user_email VARCHAR(100);
    
    -- 声明游标
    DECLARE cur_users CURSOR FOR 
        SELECT username, email FROM users;
    
    -- 声明结束处理
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    
    -- 打开游标
    OPEN cur_users;
    
    -- 循环遍历
    read_loop: LOOP
        FETCH cur_users INTO user_name, user_email;
        IF done THEN
            LEAVE read_loop;
        END IF;
        SELECT user_name, user_email;
    END LOOP;
    
    -- 关闭游标
    CLOSE cur_users;
END //
DELIMITER ;
▶ 试一试

7. 存储函数

▶ 示例:创建函数

SQL
DELIMITER //
CREATE FUNCTION fn_calc_bonus(salary DECIMAL(10,2))
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
    DECLARE bonus DECIMAL(10,2);
    
    IF salary > 10000 THEN
        SET bonus = salary * 0.2;
    ELSEIF salary > 5000 THEN
        SET bonus = salary * 0.1;
    ELSE
        SET bonus = salary * 0.05;
    END IF;
    
    RETURN bonus;
END //
DELIMITER ;

-- 使用函数
SELECT name, salary, fn_calc_bonus(salary) AS bonus FROM employees;
▶ 试一试

8. 管理存储过程

▶ 示例:查看和删除

SQL
-- 查看所有存储过程
SHOW PROCEDURE STATUS WHERE Db = 'mydb';

-- 查看创建语句
SHOW CREATE PROCEDURE sp_get_user;

-- 删除存储过程
DROP PROCEDURE IF EXISTS sp_get_user;

-- 删除函数
DROP FUNCTION IF EXISTS fn_calc_bonus;
▶ 试一试

❓ 常见问题

Q 存储过程和函数有什么区别?
A 存储过程用 CALL 调用,不返回值;函数用 SELECT 调用,必须返回一个值。
Q DELIMITER 是什么?
A 修改语句结束符。因为存储过程中有 ;,需要换成 // 避免提前结束。
Q 存储过程能提高性能?
A 存储过程预编译,减少网络传输。但现代 MySQL 优化器已经很好,性能提升有限。
Q 存储过程和函数区别?
A 过程用 CALL 调用可有 OUT 参数不返回值,函数在 SELECT 中使用必须 RETURN 一个值。
Q 存储过程会被 SQL 注入吗?
A 用 PREPARE+CONCAT 拼接 SQL 会注入,参数传递方式安全。

📖 小节


📝 作业

  1. 基础题(难度⭐):创建一个存储过程,输入用户 ID 返回用户信息。

  2. 进阶题(难度⭐⭐):创建带 IF 判断的存储过程,根据分数返回等级。

  3. 挑战题(难度⭐⭐⭐):用游标遍历用户表,计算并更新每个用户的积分。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏