存储过程:一组预编译的SQL语句,存储在数据库中,可以重复调用。
东巴文理解:
存储过程 = 菜谱
菜谱记录了做菜的步骤
存储过程记录了SQL的执行步骤
按照菜谱做菜,按照存储过程执行SQL
优点:
缺点:
语法格式:
CREATE PROCEDURE 存储过程名([参数])
BEGIN
SQL语句;
END;
示例:
-- 修改分隔符
DELIMITER //
-- 创建存储过程
CREATE PROCEDURE get_all_employees()
BEGIN
SELECT * FROM employees;
END //
-- 恢复分隔符
DELIMITER ;
语法格式:
CALL 存储过程名([参数]);
示例:
-- 调用存储过程
CALL get_all_employees();
特点:输入参数,只读
示例:
DELIMITER //
CREATE PROCEDURE get_employee_by_id(IN emp_id INT)
BEGIN
SELECT * FROM employees WHERE id = emp_id;
END //
DELIMITER ;
-- 调用
CALL get_employee_by_id(1);
特点:输出参数,可写
示例:
DELIMITER //
CREATE PROCEDURE get_employee_count(OUT emp_count INT)
BEGIN
SELECT COUNT(*) INTO emp_count FROM employees;
END //
DELIMITER ;
-- 调用
CALL get_employee_count(@count);
SELECT @count;
特点:输入输出参数,可读可写
示例:
DELIMITER //
CREATE PROCEDURE double_value(INOUT num INT)
BEGIN
SET num = num * 2;
END //
DELIMITER ;
-- 调用
SET @num = 10;
CALL double_value(@num);
SELECT @num; -- 输出:20
语法格式:
DROP PROCEDURE [IF EXISTS] 存储过程名;
示例:
-- 删除存储过程
DROP PROCEDURE IF EXISTS get_all_employees;
-- 查看指定数据库的存储过程
SELECT ROUTINE_NAME, ROUTINE_TYPE
FROM information_schema.ROUTINES
WHERE ROUTINE_SCHEMA = 'database_name';
-- 查看存储过程定义
SHOW CREATE PROCEDURE get_all_employees;
语法格式:
DECLARE 变量名 数据类型 [DEFAULT 默认值];
示例:
DELIMITER //
CREATE PROCEDURE test_variable()
BEGIN
-- 声明变量
DECLARE name VARCHAR(50) DEFAULT '张三';
DECLARE age INT DEFAULT 25;
SELECT name, age;
END //
DELIMITER ;
-- 调用
CALL test_variable();
使用SET:
DELIMITER //
CREATE PROCEDURE test_set()
BEGIN
DECLARE name VARCHAR(50);
SET name = '李四';
SELECT name;
END //
DELIMITER ;
使用SELECT INTO:
DELIMITER //
CREATE PROCEDURE test_select_into(IN emp_id INT)
BEGIN
DECLARE emp_name VARCHAR(50);
DECLARE emp_salary DECIMAL(10, 2);
SELECT name, salary INTO emp_name, emp_salary
FROM employees WHERE id = emp_id;
SELECT emp_name, emp_salary;
END //
DELIMITER ;
语法格式:
SET @变量名 = 值;
-- 或
SET @变量名 := 值;
示例:
-- 定义用户变量
SET @name = '张三';
SET @age := 25;
-- 使用用户变量
SELECT @name, @age;
-- 在SQL中使用
SELECT * FROM employees WHERE name = @name;
-- 查看所有系统变量
SHOW VARIABLES;
-- 查看特定系统变量
SHOW VARIABLES LIKE 'max_connections';
会话级别:
SET SESSION max_connections = 200;
-- 或
SET @@session.max_connections = 200;
全局级别:
SET GLOBAL max_connections = 200;
-- 或
SET @@global.max_connections = 200;
语法格式:
IF 条件 THEN
语句;
ELSEIF 条件 THEN
语句;
ELSE
语句;
END IF;
示例:
DELIMITER //
CREATE PROCEDURE get_grade(IN score DECIMAL(5, 2), OUT grade CHAR(1))
BEGIN
IF score >= 90 THEN
SET grade = 'A';
ELSEIF score >= 80 THEN
SET grade = 'B';
ELSEIF score >= 70 THEN
SET grade = 'C';
ELSEIF score >= 60 THEN
SET grade = 'D';
ELSE
SET grade = 'E';
END IF;
END //
DELIMITER ;
-- 调用
CALL get_grade(85, @grade);
SELECT @grade; -- 输出:B
语法格式:
CASE
WHEN 条件1 THEN 语句1
WHEN 条件2 THEN 语句2
ELSE 语句3
END CASE;
示例:
DELIMITER //
CREATE PROCEDURE get_season(IN month INT, OUT season VARCHAR(10))
BEGIN
CASE
WHEN month IN (3, 4, 5) THEN
SET season = '春季';
WHEN month IN (6, 7, 8) THEN
SET season = '夏季';
WHEN month IN (9, 10, 11) THEN
SET season = '秋季';
WHEN month IN (12, 1, 2) THEN
SET season = '冬季';
ELSE
SET season = '无效月份';
END CASE;
END //
DELIMITER ;
-- 调用
CALL get_season(5, @season);
SELECT @season; -- 输出:春季
语法格式:
[标签:] LOOP
语句;
IF 退出条件 THEN
LEAVE 标签;
END IF;
END LOOP [标签];
示例:
DELIMITER //
CREATE PROCEDURE test_loop(OUT result INT)
BEGIN
DECLARE i INT DEFAULT 0;
SET result = 0;
add_loop: LOOP
SET i = i + 1;
SET result = result + i;
IF i >= 10 THEN
LEAVE add_loop;
END IF;
END LOOP add_loop;
END //
DELIMITER ;
-- 调用
CALL test_loop(@result);
SELECT @result; -- 输出:55
语法格式:
[标签:] WHILE 条件 DO
语句;
END WHILE [标签];
示例:
DELIMITER //
CREATE PROCEDURE test_while(OUT result INT)
BEGIN
DECLARE i INT DEFAULT 0;
SET result = 0;
WHILE i < 10 DO
SET i = i + 1;
SET result = result + i;
END WHILE;
END //
DELIMITER ;
-- 调用
CALL test_while(@result);
SELECT @result; -- 输出:55
语法格式:
[标签:] REPEAT
语句;
UNTIL 条件 END REPEAT [标签];
示例:
DELIMITER //
CREATE PROCEDURE test_repeat(OUT result INT)
BEGIN
DECLARE i INT DEFAULT 0;
SET result = 0;
REPEAT
SET i = i + 1;
SET result = result + i;
UNTIL i >= 10 END REPEAT;
END //
DELIMITER ;
-- 调用
CALL test_repeat(@result);
SELECT @result; -- 输出:55
ITERATE:跳过当前循环的剩余语句,进入下一次循环。
示例:
DELIMITER //
CREATE PROCEDURE test_iterate(OUT result INT)
BEGIN
DECLARE i INT DEFAULT 0;
SET result = 0;
add_loop: LOOP
SET i = i + 1;
-- 跳过偶数
IF i % 2 = 0 THEN
ITERATE add_loop;
END IF;
SET result = result + i;
IF i >= 10 THEN
LEAVE add_loop;
END IF;
END LOOP add_loop;
END //
DELIMITER ;
-- 调用
CALL test_iterate(@result);
SELECT @result; -- 输出:25(1+3+5+7+9)
游标:用于遍历查询结果集的指针。
东巴文理解:
游标 = 书签
看书时用书签标记位置
游标标记结果集的当前位置
可以逐行读取数据
语法格式:
DECLARE 游标名 CURSOR FOR SELECT语句;
语法格式:
OPEN 游标名;
语法格式:
FETCH 游标名 INTO 变量列表;
语法格式:
CLOSE 游标名;
DELIMITER //
CREATE PROCEDURE process_employees()
BEGIN
-- 声明变量
DECLARE done INT DEFAULT FALSE;
DECLARE emp_id INT;
DECLARE emp_name VARCHAR(50);
DECLARE emp_salary DECIMAL(10, 2);
-- 声明游标
DECLARE emp_cursor CURSOR FOR
SELECT id, name, salary FROM employees;
-- 声明结束处理程序
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
-- 创建临时表存储结果
CREATE TEMPORARY TABLE IF NOT EXISTS emp_results (
id INT,
name VARCHAR(50),
salary DECIMAL(10, 2),
salary_level VARCHAR(10)
);
-- 清空临时表
TRUNCATE TABLE emp_results;
-- 打开游标
OPEN emp_cursor;
-- 循环读取数据
read_loop: LOOP
FETCH emp_cursor INTO emp_id, emp_name, emp_salary;
-- 判断是否结束
IF done THEN
LEAVE read_loop;
END IF;
-- 处理数据
IF emp_salary >= 15000 THEN
INSERT INTO emp_results VALUES (emp_id, emp_name, emp_salary, '高');
ELSEIF emp_salary >= 10000 THEN
INSERT INTO emp_results VALUES (emp_id, emp_name, emp_salary, '中');
ELSE
INSERT INTO emp_results VALUES (emp_id, emp_name, emp_salary, '低');
END IF;
END LOOP;
-- 关闭游标
CLOSE emp_cursor;
-- 返回结果
SELECT * FROM emp_results;
-- 删除临时表
DROP TEMPORARY TABLE IF EXISTS emp_results;
END //
DELIMITER ;
-- 调用
CALL process_employees();
存储函数:返回单个值的存储过程。
与存储过程的区别:
| 特性 | 存储过程 | 存储函数 |
|---|---|---|
| 返回值 | 可以返回多个值 | 必须返回单个值 |
| 参数 | IN/OUT/INOUT | 只有IN |
| 调用方式 | CALL | SELECT |
| SQL语句 | 可以包含所有SQL | 不能包含动态SQL |
语法格式:
CREATE FUNCTION 函数名([参数])
RETURNS 返回类型
[DETERMINISTIC|NOT DETERMINISTIC]
BEGIN
SQL语句;
RETURN 返回值;
END;
参数说明:
示例:
DELIMITER //
CREATE FUNCTION get_employee_name(emp_id INT)
RETURNS VARCHAR(50)
DETERMINISTIC
BEGIN
DECLARE emp_name VARCHAR(50);
SELECT name INTO emp_name FROM employees WHERE id = emp_id;
RETURN emp_name;
END //
DELIMITER ;
-- 调用
SELECT get_employee_name(1);
DELIMITER //
CREATE FUNCTION calculate_tax(salary DECIMAL(10, 2))
RETURNS DECIMAL(10, 2)
DETERMINISTIC
BEGIN
DECLARE tax DECIMAL(10, 2);
IF salary <= 5000 THEN
SET tax = 0;
ELSEIF salary <= 10000 THEN
SET tax = (salary - 5000) * 0.1;
ELSEIF salary <= 20000 THEN
SET tax = 500 + (salary - 10000) * 0.2;
ELSE
SET tax = 2500 + (salary - 20000) * 0.3;
END IF;
RETURN tax;
END //
DELIMITER ;
-- 调用
SELECT name, salary, calculate_tax(salary) AS tax
FROM employees;
DELIMITER //
CREATE FUNCTION mask_phone(phone VARCHAR(20))
RETURNS VARCHAR(20)
DETERMINISTIC
BEGIN
RETURN CONCAT(
LEFT(phone, 3),
'****',
RIGHT(phone, 4)
);
END //
DELIMITER ;
-- 调用
SELECT name, mask_phone(phone) AS masked_phone
FROM employees;
语法格式:
DROP FUNCTION [IF EXISTS] 函数名;
示例:
-- 删除存储函数
DROP FUNCTION IF EXISTS get_employee_name;
-- 查看指定数据库的存储函数
SELECT ROUTINE_NAME, ROUTINE_TYPE
FROM information_schema.ROUTINES
WHERE ROUTINE_SCHEMA = 'database_name' AND ROUTINE_TYPE = 'FUNCTION';
-- 查看存储函数定义
SHOW CREATE FUNCTION get_employee_name;
语法格式:
DECLARE 处理类型 HANDLER FOR 错误类型 处理语句;
处理类型:
错误类型:
示例1:处理NOT FOUND
DELIMITER //
CREATE PROCEDURE test_handler1()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE emp_name VARCHAR(50);
DECLARE emp_cursor CURSOR FOR SELECT name FROM employees;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN emp_cursor;
read_loop: LOOP
FETCH emp_cursor INTO emp_name;
IF done THEN
LEAVE read_loop;
END IF;
SELECT emp_name;
END LOOP;
CLOSE emp_cursor;
END //
DELIMITER ;
示例2:处理SQLEXCEPTION
DELIMITER //
CREATE PROCEDURE test_handler2()
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
SELECT '事务已回滚' AS message;
END;
START TRANSACTION;
INSERT INTO employees (id, name) VALUES (1, '张三');
INSERT INTO employees (id, name) VALUES (1, '李四'); -- 主键冲突
COMMIT;
END //
DELIMITER ;
语法格式:
SIGNAL SQLSTATE '状态码'
SET MESSAGE_TEXT = '错误信息';
示例:
DELIMITER //
CREATE PROCEDURE withdraw(IN acc_id INT, IN amount DECIMAL(10, 2))
BEGIN
DECLARE balance DECIMAL(10, 2);
SELECT balance INTO balance FROM accounts WHERE id = acc_id;
IF balance < amount THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '余额不足';
END IF;
UPDATE accounts SET balance = balance - amount WHERE id = acc_id;
END //
DELIMITER ;
DELIMITER //
CREATE PROCEDURE batch_insert(IN num INT)
BEGIN
DECLARE i INT DEFAULT 1;
WHILE i <= num DO
INSERT INTO employees (name, salary, dept_id)
VALUES (CONCAT('员工', i), RAND() * 10000 + 5000, (i % 4) + 1);
SET i = i + 1;
END WHILE;
SELECT CONCAT('成功插入', num, '条数据') AS message;
END //
DELIMITER ;
-- 调用
CALL batch_insert(100);
DELIMITER //
CREATE PROCEDURE page_query(
IN table_name VARCHAR(50),
IN page_num INT,
IN page_size INT
)
BEGIN
DECLARE offset_val INT;
SET offset_val = (page_num - 1) * page_size;
SET @sql = CONCAT('SELECT * FROM ', table_name, ' LIMIT ', offset_val, ', ', page_size);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END //
DELIMITER ;
-- 调用
CALL page_query('employees', 2, 10);
DELIMITER //
CREATE PROCEDURE dept_statistics()
BEGIN
SELECT
d.dept_name,
COUNT(e.id) AS emp_count,
IFNULL(AVG(e.salary), 0) AS avg_salary,
IFNULL(MAX(e.salary), 0) AS max_salary,
IFNULL(MIN(e.salary), 0) AS min_salary
FROM departments d
LEFT JOIN employees e ON d.id = e.dept_id
GROUP BY d.id, d.dept_name;
END //
DELIMITER ;
-- 调用
CALL dept_statistics();
✅ 存储过程是一组预编译的SQL语句 ✅ 参数类型包括IN、OUT、INOUT ✅ 流程控制包括IF、CASE、LOOP、WHILE、REPEAT ✅ 游标用于遍历结果集 ✅ 存储函数返回单个值
完成本章学习后,请确认您能够: