479 字
1 分钟
第 7 章 MySQL 程序设计
本章主要介绍存储过程、自定义函数、触发器以及游标。
7.1 MySQL 编程基础
在编写存储过程和函数时,变量分为三类:
- 系统变量:以
@@开头,如@@datadir、@@sql_safe_updates。 - 用户变量:以
@开头,在当前会话(Session)中一直有效,无需声明即可直接赋值使用(如SET @num = 1;)。 - 局部变量:必须在存储过程或函数体的
BEGIN...END块中使用DECLARE声明,且声明必须位于块的最开始处。
7.2 存储过程(Stored Procedure) 与自定义函数 (UDF)* 存储过程:
* 通过 `CREATE PROCEDURE` 创建,使用 `CALL` 语句调用。* 支持 `IN`(输入)、`OUT`(输出)、`INOUT`(输入输出)三种参数模式。* 可以包含 DDL、DML 操作,允许返回多个结果集。- 自定义函数:
- 通过
CREATE FUNCTION创建,可以直接在 SQL 语句中调用(如SELECT my_func(col) FROM tbl;)。 - 只支持输入参数,必须 有
RETURNS说明和RETURN返回值。 - 函数体内部限制较多,不能包含导致数据修改的事务性 DDL 操作,且不能返回结果集。
- 通过
-- 存储过程实例:计算商品单价中位数、方差与标准差DROP PROCEDURE IF EXISTS sp_price_stats;DELIMITER $$CREATE PROCEDURE sp_price_stats()BEGIN DECLARE $n INT; DECLARE $m, $s, $x DOUBLE;
-- 获取总数、平方和与平均值 SELECT COUNT(*), SUM(Unitprice * Unitprice), AVG(Unitprice) INTO $n, $s, $x FROM Products;
-- 计算中位数(区分奇偶数) IF (MOD($n, 2) = 0) THEN SELECT SUM(Unitprice) / 2 INTO $m FROM ( SELECT Unitprice, ROW_NUMBER() OVER (ORDER BY Unitprice) as rowno FROM Products ) as p WHERE rowno = $n / 2 OR rowno = $n / 2 + 1; ELSE SELECT Unitprice INTO $m FROM ( SELECT Unitprice, ROW_NUMBER() OVER (ORDER BY Unitprice) as rowno FROM Products ) as p WHERE rowno = ($n + 1) / 2; END IF;
-- 输出统计值 SELECT $m as '中位数', CAST(($s - $n * $x * $x) / $n as DECIMAL(16,8)) as '方差', CONVERT(SQRT(($s - $n * $x * $x) / $n), DECIMAL(16,8)) as '标准差';END $$DELIMITER ;7.3 触发器(Trigger)触发器是在表发生 INSERT、UPDATE 或 DELETE 操作时自动触发执行的一组 SQL 语句。
- 行级触发器:使用
FOR EACH ROW说明,每一次受影响的行都会执行一次触发器。 - 内置临时行:
INSERT触发器:拥有NEW临时行,代表即将插入的新数据。DELETE触发器:拥有OLD临时行,代表即将删除的历史数据。UPDATE触发器:同时拥有OLD(修改前)和NEW(修改后)临时行。
- 异常终止机制:在触发器中如果需要终止当前操作(例如业务校验失败),可以使用
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '错误信息';抛出异常以强行回滚当前 SQL 操作。
-- 触发器实例:当折扣率超出客户类型的限制上限时,使用 SIGNAL 强制抛出异常回滚操作DROP TRIGGER IF EXISTS tr_check_discount;DELIMITER $$CREATE TRIGGER tr_check_discount BEFORE INSERT ON myOrderitemsFOR EACH ROWBEGIN DECLARE $limit DECIMAL(6,2);
-- 根据订单号查询对应客户类别的折扣上限 SELECT Discount_limit INTO $limit FROM Orders JOIN Customers USING (CustomerID) JOIN CustomerTypes USING (TypeID) WHERE OrderID = new.OrderID;
-- 校验折扣是否越界 IF (new.Discount IS NOT NULL) AND (new.Discount < 0 OR new.Discount > $limit) THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '插入失败:销售折扣率超出该客户类别的上限约束!'; END IF;END $$DELIMITER ;7.4 游标(Cursor)游标用于逐行遍历 SQL 查询结果集。MySQL 的游标是 只读、单向(不可逆向移动)且不敏感 的。
使用步骤:DECLARE(定义游标及 NOT FOUND 处理器) OPEN(打开游标) FETCH(提取数据到变量) CLOSE(关闭游标)。
-- 游标循环标准模板(使用 WHILE 循环)DROP PROCEDURE IF EXISTS sp_cursor_demo;DELIMITER $$CREATE PROCEDURE sp_cursor_demo()BEGIN DECLARE $done INT DEFAULT 0; DECLARE $pid INT; DECLARE $price DECIMAL(10,2);
-- 1. 声明游标 DECLARE cur_prod CURSOR FOR SELECT ProductID, Unitprice FROM Products; -- 2. 声明 NOT FOUND 异常句柄,游标读完后自动将 $done 置为 1 DECLARE CONTINUE HANDLER FOR NOT FOUND SET $done = 1;
OPEN cur_prod;
-- 3. 首次提取数据 FETCH cur_prod INTO $pid, $price;
-- 4. 循环遍历 WHILE $done = 0 DO -- 执行业务逻辑处理 -- ...
-- 下移游标指针 FETCH cur_prod INTO $pid, $price; END WHILE;
CLOSE cur_prod;END $$DELIMITER ; 分享
如果这篇文章对你有帮助,欢迎分享给更多人!
第 7 章 MySQL 程序设计
https://blog.sopak.space/posts/study/economics-management/dp-a/7/ 部分信息可能已经过时
相关文章 猜你想看

