


专栏:MySQL数据库成长记
个人主页:手握风云
目录
[1.1. 语法](#1.1. 语法)
[1.2. 示例](#1.2. 示例)
[2.1. 触发器是什么](#2.1. 触发器是什么)
[2.2. 语法](#2.2. 语法)
[2.3. 示例](#2.3. 示例)
一、存储函数
1.1. 语法
MySQL 存储函数是有返回值的存储过程,参数只能是 IN 类型(无需显式声明 IN,默认即为输入参数,不支持 OUT 和 INPUT 类型),类似于内置函数。存储函数与存储过程的主要区别在于存储函数必须包含 RETURN 语句,且返回值类型需与 CREATE FUNCTION 中 RETURNS 指定的类型一致,而存储过程则不⼀定。
sql
-- 存储函数创建语法
CREATE FUNCTION 存储函数名称 ([参数列表])
RETURNS type [characteristic ...]
BEGIN
-- SQL语句
RETURN ...;
END;
-- characteristic(特性)选项说明
[NOT] DETERMINISTIC --表示相同的输入参数总是产生[不同]相同的结果
| NO SQL --不包含SQL语句
| READS SQL DATA --包含读取数据的语句,如select
| MODIFIES SQL DATA -- 包含写入数据的语句,如update,delete
-- 存储函数调用语法
select 存储函数名称 ([参数列表]);
1.2. 示例
sql
-- 使用存储函数实现
-- 传入一个 n, 计算从 1 到 n 累加的值
delimiter //
CREATE FUNCTION fun1(n INT) RETURNS INT
BEGIN
-- 定义变量保存结果
DECLARE total INT DEFAULT 0;
-- 循环
WHILE n > 0 DO
SET total := total + n;
SET n := n - 1;
END WHILE;
RETURN total;
END//
delimiter ;
-- 调用存储函数
SELECT fun1(100);
但是我们第一次运行就会出错,这是因为在 MySQL 8.0 版本中,如果 binlog 是开启的,那么在定义存储函数时,需要指定characteristic特 性,否则会报错。

sql
-- 使用存储函数实现
-- 传入一个 n, 计算从 1 到 n 累加的值
delimiter //
CREATE FUNCTION fun1(n INT) RETURNS INT DETERMINISTIC
BEGIN
-- 定义变量保存结果
DECLARE total INT DEFAULT 0;
-- 循环
WHILE n > 0 DO
SET total := total + n;
SET n := n - 1;
END WHILE;
RETURN total;
END//
delimiter ;
-- 调用存储函数
SELECT fun1(100);
二、触发器
2.1. 触发器是什么
触发器是 MySQL 中与表强关联 的数据库对象,其核心是在对关联表执行 INSERT(插入)、UPDATE(更新)、DELETE(删除)三类特定操作时,自动触发并执行预先定义的 SQL 语句或逻辑块,无需手动调用,常用于实现数据验证、业务逻辑强制、操作日志记录等需求。就如同在点餐的时候,当用户向商家付钱时,就会触发商家记账的过程,而商家可以根据记账的信息判断自己需要进货、盈利还是亏损。

触发器的触发需满足 "触发时间 " 和 "触发操作" 两个条件:
- 触发时间 :分为
BEFORE(操作执行前触发)和AFTER(操作执行后触发),可根据业务需求选择(如数据校验适合BEFORE,日志记录适合AFTER)。 - 触发操作:仅支持 INSERT、UPDATE、DELETE 三种表操作,即只有这三类操作能触发触发器。

通过 OLD 和 NEW 关键字可访问操作前后的数据,不同触发操作对应的关键字使用规则不同
|-------------|----------------|----------------|
| 触发器类型(触发操作) | OLD 关键字含义 | NEW 关键字含义 |
| INSERT 触发器 | 无(插入操作无 "旧数据") | 将要或已经插入的新数据 |
| UPDATE 触发器 | 数据更新前的旧值 | 将要或已经更新后的新值 |
| DELETE 触发器 | 将要或已经删除的旧数据 | 无(删除操作无 "新数据") |
触发器按 "触发粒度" 可分为行级触发器 和语句级触发器,二者的核心区别在于 "触发次数" 和 "数据访问能力":
| 类型 | 触发逻辑 | 数据访问能力 | MySQL 支持情况 |
|---|---|---|---|
| 行级触发器 | 对表中每一行数据 执行操作时,均触发一次(如UPDATE语句影响 10 行,则触发 10 次) |
可通过OLD/NEW访问当前行的新旧值 |
仅支持行级触发器 |
| 语句级触发器 | 整个 INSERT/UPDATE/DELETE 语句执行时,仅触发一次(无论影响多少行) | 无法访问单条数据的新旧值,仅能处理全局逻辑 | 不支持 |
2.2. 语法
sql
-- 创建
CREATE TRIGGER [IF NOT EXISTS] trigger_name
trigger_time trigger_event
ON tbl_name FOR EACH ROW
BEGIN
trigger_stmt;
END;
trigger_time: { BEFORE | AFTER }
trigger_event: { INSERT | UPDATE | DELETE }
-- 查看
SHOW TRIGGERS;
-- 删除,如果没有指定schema_name,默认为当前数据库
DROP TRIGGER [IF EXISTS] [schema_name.]trigger_name;
2.3. 示例
sql
-- 创建学生日志表
CREATE TABLE student_log (
id BIGINT PRIMARY KEY auto_increment,
operation_type VARCHAR(10) NOT NULL COMMENT '操作类型: INSERT/UPDATE/DELETE',
operation_time DATETIME NOT NULL COMMENT '操作时间',
operation_id BIGINT NOT NULL COMMENT '操作的记录ID',
operation_data VARCHAR(500) comment '操作数据'
);
- 插入数据的触发器
sql
delimiter //
CREATE TRIGGER IF NOT EXISTS trg_student_insert
AFTER INSERT ON student FOR EACH ROW
BEGIN
-- 插入日志到 student_log 表
INSERT INTO student_log (
operation_type,
operation_time,
operation_id,
operation_data
) VALUES (
'INSERT',
NOW(),
new.id,
CONCAT(new.id,',', new.name, ',', new.sno, ',', new.gender, ',', new.enroll_date, ',', new.class_id)
);
END//
insert into student values (null, '曹操', '300001', 28, 1, '2025-09-01', 3);
- 更新数据的触发器
sql
delimiter //
CREATE TRIGGER IF NOT EXISTS trg_student_update
AFTER UPDATE ON student FOR EACH ROW
BEGIN
-- 插入日志到 student_log 表
INSERT INTO student_log (
operation_type,
operation_time,
operation_id,
operation_data
) VALUES (
'UPDATE',
NOW(),
new.id,
CONCAT(old.id,',', old.name, ',', old.sno, ',', old.gender, ',', old.enroll_date, ',', old.class_id)
);
END//
delimiter ;
update student set age = 20, class_id = 2 where name = '曹操';
sql
-- 更新多条数据
update student set class_id = 3 where id >= 7;

- 删除数据的触发器
sql
delimiter //
CREATE TRIGGER IF NOT EXISTS trg_student_delete
AFTER DELETE ON student FOR EACH ROW
BEGIN
-- 插入日志到 student_log 表
INSERT INTO student_log (
operation_type,
operation_time,
operation_id,
operation_data
) VALUES (
'DELETE',
NOW(),
old.id,
CONCAT(old.id,',', old.name, ',', old.sno, ',', old.gender, ',', old.enroll_date, ',', old.class_id)
);
END//
delimiter ;
delete from student where name = '曹操';