前面我们学习了存储过程、if/case/while/repeat/loop 等流程控制语句,本节继续学习触发器、自定义存储函数,这两块是 MySQL 编程里非常常用的内容,常用于数据同步、数据校验、封装计算逻辑。
1.1 触发器(Trigger)
1.1.1 触发器简介
触发器是一种特殊的存储过程。存储过程需要我们手动使用 call 调用执行;而触发器是由表事件自动触发执行 ,当对绑定的表执行 insert、delete、update 操作时,触发器自动激活运行。
触发器常用来实现:数据完整性校验、跨表数据同步、日志记录、业务规则强制约束。
举个典型场景:学生表新增一条学生记录时,自动更新统计表里的学生总人数,保证统计数据和真实记录一致。
核心特点:触发器一定依附于某一张数据表,监控这张表的增删改事件。
1.1.2 创建触发器语法
1.只有一条执行语句的触发器
create trigger 触发器名 before|after 触发事件
on 表名 for each row
执行语句;
2.多条执行语句的触发器(需要 begin ... end,配合修改语句结束符 delimiter)
create trigger 触发器名 before|after 触发事件
on 表名 for each row
begin
执行语句列表;
end;
参数说明:
before / after:触发时机。before 在增 / 改 / 删之前 执行;after 在操作之后执行insert / update / delete:触发事件,也就是监控什么 SQL 动作on 表名:触发器绑定的数据表for each row:行级触发器,每修改一行数据,触发器就执行一次(MySQL 只有行级触发器)- 程序体:可以写普通 SQL,也可以搭配 if、循环等流程控制
1.1.3 实操示例:学生人数统计触发器
1.建库建表,初始化数据
create database mydb16_trigger;
use mydb16_trigger;
-- 学生表
create table student1(
id int unsigned auto_increment primary key not null,
name varchar(50)
);
insert into student1(name) values('jack');
-- 学生总数统计表
create table student_total(total int);
insert into student_total values(1);
2.创建触发器:新增学生后,总数 + 1
create trigger student_insert_trigger
after insert on student1
for each row
update student_total set total=total+1;
3.测试触发器
insert into student1(name) values('jenny');
select * from student_total;
结果:total 自动变为 2。
4.查看触发器
-- 方式1
show triggers\G
-- 方式2,查询系统库information_schema
use information_schema;
select * from triggers\G
select * from triggers where trigger_name='student_insert_trigger'\G
5.删除触发器
drop trigger if exists student_insert_trigger;
1.1.4 NEW 和 OLD 关键字
在触发器内部,通过 NEW、OLD 获取本次被操作行的数据:
| 触发器类型 | 可用关键字 | 含义 |
|---|---|---|
| INSERT | NEW | 即将 / 已经插入的新行数据 |
| UPDATE | OLD、NEW | OLD:修改前旧数据;NEW:修改后新数据 |
| DELETE | OLD | 即将 / 已经被删除的旧行数据 |
使用格式:NEW.列名、OLD.列名
案例:一张表增删改,自动同步到另一张表
use mydb16_trigger;
-- 主表
create table tab1(
id int primary key auto_increment,
name varchar(50),
sex enum('m','f'),
age int
);
-- 同步表
create table tab2(
id int primary key auto_increment,
name varchar(50),
salary double(10,2)
);
-- 新增同步触发器
create trigger tab1_after_insert_trigger
after insert on tab1 for each row
insert into tab2 values(NEW.id, NEW.name, 5000);
-- 修改同步触发器
create trigger tab1_after_update_trigger
after update on tab1 for each row
update tab2 set name=new.name where name=old.name;
-- 删除同步触发器
create trigger tab1_after_delete_trigger
after delete on tab1 for each row
delete from tab2 where name=old.name;
测试命令:
insert into tab1 values(1,"张三",'m',22);
select * from tab1;
select * from tab2;
update tab1 set name='李四' where name='张三';
select * from tab1;
select * from tab2;
delete from tab1 where name='李四';
select * from tab1;
select * from tab2;
1.1.5 触发器重要注意事项
- 触发器内部不能对本表执行 insert /update/delete,容易出现递归循环触发,造成死循环报错。
- 触发器是行级触发,批量 DML 操作(一次插入上千条)会循环触发多次,非常消耗性能。
- 高并发、频繁增删改的业务表,尽量少用触发器;触发器逻辑隐蔽,排错困难,业务逻辑优先写在应用层。
2.1 存储函数(自定义函数 function)
2.1.1 概念
存储函数,也叫自定义函数,用于封装一段计算逻辑,必须有且仅有一个返回值 ,可以像 MySQL 内置函数(max()、count())一样,直接写在 select 语句中使用。
存储函数 VS 存储过程对比 | 对比项 | 存储函数 function | 存储过程 procedure | | ---- | ---- | ---- | | 返回值 | 有且只有 1 个返回值 | 可以多个 / 无返回值(in/out/inout) | | 参数 | 仅支持入参,不需要写 in | in /out/inout 三种参数 | | 内部限制 | 限制多,一般不允许 DML(insert/update/delete) | 几乎无限制,可以写复杂业务、DML 语句 | | 调用方式 | select 函数名 (); | call 存储过程名 (); | | 用途 | 计算、查询,返回单个结果 | 复杂业务流程、批量操作 |
补充:存储过程可以调用函数;函数不能调用存储过程。
2.1.2 创建、调用、查看存储函数
创建语法:
create function 函数名([参数名 类型,...])
returns 返回值类型
[函数特性]
begin
函数体;
end;
returns:必填,指定返回的数据类型- 函数体中必须有
return返回数据
提示:MySQL 开启 binlog 时,创建函数会报错,先执行一次:
set global log_bin_trust_function_creators=TRUE;
示例 1:无参存储函数,统计表 emp_new 总人数
delimiter $$
create function myfunc1_emp()
returns int
begin
declare cnt int default 0;
select count(*) into cnt from emp_new;
return cnt;
end $$
delimiter ;
-- 调用
select myfunc1_emp();
示例 2:带参数存储函数,根据 sid 查询员工姓名
delimiter $$
create function myfunc2_emp(in_sid int)
returns varchar(50)
begin
declare out_name varchar(50);
select name into out_name from emp_new where sid = in_sid;
return out_name;
end $$
delimiter ;
-- 调用
select myfunc2_emp(1776);
查看存储函数
-- 查看函数状态
show function status like 'myfunc%';
-- 查看函数创建源码
show create function myfunc2_emp;
-- 系统表查询
select * from information_schema.ROUTINES
where ROUTINE_NAME='myfunc2_emp' AND ROUTINE_TYPE='FUNCTION'\G
2.1.3 删除存储函数
drop function if exists myfunc1_emp;
drop function if exists myfunc2_emp;
小结
- 触发器是事件驱动,绑定在表上,行级触发,适合轻量数据同步,慎用在高写入场景;核心就是
NEW和OLD获取变更前后的数据。 - 存储函数侧重计算,有返回值,可以嵌入 select;存储过程适合复杂业务逻辑,用 call 调用。
- MySQL 编程三件套:存储过程(流程)、触发器(自动事件)、自定义函数(计算封装),生产环境不要滥用,优先业务层实现逻辑,方便维护和排查性能问题。