MySQL数据库精研之旅第二十期:存储过程,数据处理的全能工具箱(三)

专栏: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 = '曹操';
相关推荐
余槐i4 小时前
PostgreSQL 函数索引报错 must be immutable:3种绕法与代价
数据库·后端·sql·postgresql·性能优化
数据库小学妹4 小时前
PostgreSQL表膨胀怎么办?autovacuum清不动的根因与排查命令
数据库·postgresql·表膨胀·mvcc·数据库运维
mister_guo4 小时前
MySQL-1267错误:排序规则冲突的原因与解决方案
数据库·mysql
骇客野人5 小时前
在麒麟操作系统上 分别安装 jdk1.8, 达梦数据库,redis ,memcache 的操作实施方案和步骤
数据库·redis·memcached
美狐美颜SDK开放平台6 小时前
直播APP如何实现美白滤镜功能?视频美颜SDK技术开发方案详解
前端·数据库·人工智能·音视频·美颜sdk·直播美颜sdk·视频美颜sdk
Lost_the_wind6 小时前
MySQL学习笔记五
mysql
花间相见8 小时前
【计算基础|MySQL01】【上手】安装连接、数据类型与建库建表
数据库·后端
花间相见8 小时前
【计算基础|MySQL02】【上手】SQL 增删改查
数据库·后端
ITxiaobing20238 小时前
IP 库与 AppsFlyer 国家/地区数据不一致,如何系统地对账?
数据库·网络协议·tcp/ip
这个DBA有点耶8 小时前
从时间到语义:时序数据库与向量数据库的核心机制、选型与多模融合趋势
数据库·mysql·架构