MySQL高级篇---触发器应用

MySQL触发器基本知识

1、基本语法

触发器创建语法四要素:

  1. 监视地点(table)
  2. 监视事件(insert/update/delete)
  3. 触发时间(after/before)
  4. 触发事件(insert/update/delete)
CREATE TRIGGER trigger_name   // 触发器名称
trigger_time                  // 触发时机
trigger_event ON tbl_name     // trigger_event  触发事件                                      tbl_name  建立触发器的表名,                                  就是触发器是建立在哪个表上                                      的!!!
FOR EACH ROW                  // 作用域每一行
trigger_stmt                  // 触发器运行后要执行的程序题,可                                  以是一局sql语句,也可以用                                      begin 和 end  包含多条语句
  1. trigger_name:标识触发器名称,用户自行指定;
  2. trigger_time:标识触发时机,取值为 BEFORE 或 AFTER;
  3. trigger_event:标识触发事件,取值为 INSERT、UPDATE 或 DELETE;
  4. tbl_name:标识建立触发器的表名,即在哪张表上建立触发器;
  5. trigger_stmt:触发器程序体,可以是一句SQL语句,或者用 BEGIN 和 END 包含的多条语句。

通过上面的分析,我们可以得出来的结论如下:

我们可以创建6中类型的触发器

  1. before insert
  2. before update
  3. before delete
  4. after insert
  5. after update
  6. after delete

``注意`:另外有一个限制是不能同时在一个表上建立2个相同类型的触发器,因此在一个表上最多建立6个触发器。(当然,Mysql又定义了两种,分别是LOAD DATA 和 REPLACE ,MySQL是8个触发器)

2、trigger_event 详解

MySQL 除了对 INSERT、UPDATE、DELETE 基本操作进行定义外,还定义了 LOAD DATA 和 REPLACE 语句,这两种语句也能引起上述6中类型的触发器的触发。

LOAD DATA 操作:

  • LOAD DATA 语句用于将一个文件装入到一个数据表中,相当与一系列的 INSERT 操作。

REPLACE 操作

  • 语句一般来说和 INSERT 语句很像,只是在表中有 primary key 或 unique 索引时,如果插入的数据和原来 primary key 或 unique 索引一致时,会先删除原来的数据,然后增加一条新数据
  • 也就是说,一条 REPLACE 语句有时候等价于一条INSERT 语句,有时候等价于一条 DELETE 语句加上一条 INSERT 语句。
  1. INSERT 型触发器:插入某一行时激活触发器,可能通过 INSERT、LOAD DATA、REPLACE 语句触发;
  2. UPDATE 型触发器:更改某一行时激活触发器,可能通过 UPDATE 语句触发;
  3. DELETE 型触发器:删除某一行时激活触发器,可能通过 DELETE、REPLACE 语句触发。

3、BEGIN … END 详解

语法:

BEGIN
[statement_list]
END
  1. statement_list 代表一个或多个语句的列表,列表内的每条语句都必须用分号(;)来结尾。
  2. 如果直接用(;)是会报错的,原因是,在MySQL中,分号是语句结束的标识符,遇到分号表示该段语句已经结束,MySQL可以开始执行了。因此,解释器遇到statement_list 中的分号后就开始执行,然后会报出错误,因为没有找到和 BEGIN 匹配的 END。

解决上面问题的方法:

这时就会用到 DELIMITER 命令(DELIMITER 是定界符,分隔符的意思),它是一条命令,不需要语句结束标识,语法为:

DELIMITER $

在这之后的语句,以分号结束,解释器不会有什么反应,只有遇到了$,才认为是语句结束。注意,使用完之后,我们还应该记得把它给修改回来。

案例如下:
假设系统中有两个表:
班级表 class(班级号 classID, 班内学生数 stuCount)
学生表 student(学号 stuID, 所属班级号 classID)
要创建触发器来使班级表中的班内学生数随着学生的添加自动更新,代码如下:

DELIMITER $
create trigger tri_stuInsert after insert
on student for each row
begin
declare c int;
set c = (select stuCount from class where classID=new.classID);
update class set stuCount = c + 1 where classID = new.classID;
end$
DELIMITER ;

4、变量详解

MySQL 中使用 DECLARE 来定义一局部变量,该变量只能在 BEGIN … END 复合语句中使用,并且应该定义在复合语句的开头,也就是begin下的第一行!!!

语法如下:

DECLARE var_name[,...] type [DEFAULT value]
  • var_name 为变量名称,变量名不区分大小写

  • type 为 MySQL 支持的任何数据类型

  • 变量初始值为 NULL,如果需要,可以使用 DEFAULT 子句提供默认值,值可以被指定为一个表达式

  • 常用:后面的值也是可以被设计为一个表达式的返回值

    SET var_name = expr [,var_name = expr] ...
    

案例如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7zXYNDYP-1648277775968)(img/image-在这里插入图片描述
.png)]

DELIMITER $
-- 创建触发器
create trigger tri_stuInsert after insert
on `user` for each row  # 监听user表中每次增加的数据
begin                   # 开始
declare c int;          # 创建变量,来接收返回的值
set c = (select uid from `user` where uid=new.uid);  # 查询条件
insert into user_images(path,uid)VALUES('ajisdfj',c) ; # 执行插入语句
end$
DELIMITER ;

5、NEW 与 OLD 详解

DELIMITER $
-- 创建触发器
create trigger tri_stuInsert after insert
on `user` for each row  # 监听user表中每次增加的数据
begin                   # 开始
declare c int;          # 创建变量,来接收返回的值
set c = (select uid from `user` where uid=new.uid);  # 查询条件
insert into user_images(path,uid)VALUES('ajisdfj',c) ; # 执行插入语句
end$
DELIMITER ;

上述示例中使用了NEW关键字,和 MS SQL Server 中的 INSERTED 和 DELETED 类似,MySQL 中定义了 NEW 和 OLD,用来表示

触发器的所在表中,触发了触发器的那一行数据。
具体地:

  • 在 INSERT 型触发器中,NEW 用来表示将要(BEFORE)或已经(AFTER)插入的新数据
  • 在 UPDATE 型触发器中,OLD 用来表示将要或已经被修改的原数据,NEW 用来表示将要或已经修改为的新数据;
  • 在 DELETE 型触发器中,OLD 用来表示将要或已经被删除的原数据;
    使用方法: NEW.columnName (columnName 为相应数据表某一列名)
  • 另外,OLD 是只读的,而 NEW 则可以在触发器中使用 SET 赋值,这样不会再次触发触发器,造成循环调用(如每插入一个学生前,都在其学号前加“2013”)。

举例:

原本这个学生是一班的,修改时候成为二班,那么我们要让一班的学生总数减少1,让二班的总数增加1,此刻修改一班的信息就用old.uid ,修改二班的信息就用new.uid

6、查看触发器

和查看数据库(show databases;)查看表格(show tables;)一样,查看触发器的语法如下:

SHOW TRIGGERS [``FROM` `schema_name];

案例如下:
SHOW triggers from teachingmanagement;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X8ZHpGdR-1648277775970)(img/image-在这里插入图片描述
.png)]

其中,schema_name 即 Schema 的名称,在 MySQL 中 Schema 和 Database 是一样的

说人话,就是show triggers from 数据库名字, 不必先“USE database_name;”了。

7、删除触发器

和删除数据库、删除表格一样,删除触发器的语法如下:

DROP` `TRIGGER` `[IF EXISTS] [schema_name.]trigger_name


案例如下:

如果这个触发器已经存在,那么就先废除
DROP TRIGGER IF EXISTS '触发器名字';
DROP TRIGGER IF EXISTS triName;

8、触发器执行顺序

我们建立的数据库一般都是 InnoDB 数据库,其上建立的表是事务性表,也就是事务安全的。这时,若SQL语句或触发器执行失败,MySQL 会回滚事务,有:

  1. ①如果 BEFORE 触发器执行失败,SQL 无法正确执行。
  2. ②SQL 执行失败时,AFTER 型触发器不会触发。
  3. ③AFTER 类型的触发器执行失败,SQL 会回滚

MySQL触发器真实案例

1、案例一

增加操作

需求:

用户表中加入一条信息,头像表里面加一条对应的头像信息

  • 用户表数据库[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X7pl7FQU-1648277775970)(img/image-20220326134545840.png)]

  • 头像表[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WbGTLCC9-1648277775971)(img/image-在这里插入图片描述
    .png)]

问题:

头像表中需要用到插入数据的uid,也就是主键

实现思路

  1. 创建一个触发器
  2. 触发事件是user表中增加一条记录就执行
  3. 触发条件,就是user表增加记录完毕后再执行
  4. 触发执行的sql语句,需要先获取新插入的uid,然后再头像表中进行插入
--1、删除之前的触发器
drop trigger if EXISTS  t1; 
-- 
-- 基本语法如下:
-- CREATE TRIGGER trigger_name   
-- trigger_time                  
-- trigger_event ON tbl_name             
-- FOR EACH ROW                 
-- trigger_stmt  

               
--2、开始创建触发器

delimiter $
create trigger t1
after insert 
on `user` for each row
begin
-- 定义一个变量
declare c int;
-- 执行查询语句,然后将查询到的uid赋值给变量
set c = (select uid from `user` where uid=new.uid);
-- 执行插入操作,将我们的uid,路径统一插入其中即可
insert into user_images(path,uid) VALUES('/file/orgin.png',c);
end $
delimiter;


--3、查看触发器
show triggers from teachingmanagement;


--4、 在user表中执行增加操作
insert into `user`(username,passwd,number,role_id) VALUES('城市与','12345','489','1,2,3');

查看触发器:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QkZMwl8S-1648277775972)(img/image-在这里插入图片描述
.png)]

user表中执行增加操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jBeOP0Gp-1648277775972)(img/image-在这里插入图片描述
.png)]

全部成功!!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TaG2WXAs-1648277775973)(img/image-在这里插入图片描述

.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HqWV9XMt-1648277775974)(img/image-在这里插入图片描述
.png)]

坑!!1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S9DP9S3b-1648277775974)(img/image-在这里插入图片描述
.png)]

我们查询出来的是uid,并且跟我们变量名重复,会查询赋值失败,然后就会导致我下面插入的时候uid一直是null

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QmWQq8HO-1648277775975)(img/image-在这里插入图片描述
.png)]

上面这种就可以,因为变量名不一样了!!!

2、案例二

修改操作:

非常重要!!!需要明白一点,就是什么时候使用old.uid 什么时候使用new.uid

-- 开始修改user表中的uid信息,然后我们需要在头像表里面的路径
drop trigger if EXISTS t2;


delimiter $
create trigger t2
after update 
on `user` for each row
begin
declare c int;
set c = (select uid from `user` where uid=new.uid);  
--c将来要赋值给头像表中的uid,所以应该是new.uid

--这里uid=old.uid 那是因为头像表里面的uid还没有修改,仍然是之前的uid,所以我们才需要进行修改这个uid,因此这里获取到的是old.uid
update user_images set uid = c where uid = old.uid;  
-- 上面的意思就是,old.uid就是我们获取之前的uid,如果我们使用new.uid 那么获取的是修改之后的uid,之所以这么设计,就是方便我们进行两张表的操作,
-- 比如原本这个学生是一班的,修改时候成为二班,那么我们要让一班的学生总数减少1,让二班的总数增加1,此刻修改一班的信息就用old.uid ,修改二班的信息就用new.uid 
end $
delimiter;



show triggers from teachingmanagement;

-- 进行修改操作
update `user` set uid =148  where uid = 24;

.uid
update user_images set uid = c where uid = old.uid;
– 上面的意思就是,old.uid就是我们获取之前的uid,如果我们使用new.uid 那么获取的是修改之后的uid,之所以这么设计,就是方便我们进行两张表的操作,
– 比如原本这个学生是一班的,修改时候成为二班,那么我们要让一班的学生总数减少1,让二班的总数增加1,此刻修改一班的信息就用old.uid ,修改二班的信息就用new.uid
end $
delimiter;

show triggers from teachingmanagement;

– 进行修改操作
update user set uid =148 where uid = 24;

-- 开始修改user表中的uid信息,然后我们需要在头像表里面的路径
drop trigger if EXISTS t2;


delimiter $
create trigger t2
after update 
on `user` for each row
begin
declare c int;
set c = (select uid from `user` where uid=new.uid);  
--c将来要赋值给头像表中的uid,所以应该是new.uid

--这里uid=old.uid 那是因为头像表里面的uid还没有修改,仍然是之前的uid,所以我们才需要进行修改这个uid,因此这里获取到的是old.uid
update user_images set uid = c where uid = old.uid;  
--上面的意思就是,old.uid就是我们获取之前的uid,如果我们使用new.uid 那么获取的是修改之后的uid,之所以这么设计,就是方便我们进行两张表的操作,
-- 比如原本这个学生是一班的,修改时候成为二班,那么我们要让一班的学生总数减少1,让二班的总数增加1,此刻修改一班的信息就用old.uid ,修改二班的信息就用new.uid 
end $
delimiter;



show triggers from teachingmanagement;

-- 进行修改操作
update `user` set uid =148  where uid = 24;
<think>好的,我现在需要帮助用户获取关于MySQL语句的思维导图。首先,回顾用户之前的对话,用户之前已经得到了一份详细的MySQL知识点思维导图,现在用户明确表示想获取MySQL语句的思维导图,并且引用了三个相关的引用内容。 首先,我需要分析用户提供的引用内容。引用[1]提到了插入数据的示例,引用[2]是关于复杂查询的联表操作,引用[3]则似乎Vagrant相关,可能安装配置有关,但看起来不太相关。用户可能误引用了部分内容,或者引用中存在错误。因此,我需要专注于引用[1][2],因为它们直接涉及MySQL语句的示例。 接下来,用户的需求是获取MySQL语句的思维导图。考虑到用户之前已经得到了知识点的分类,现在需要将重点放在具体的SQL语句分类示例上。需要将常见的MySQL语句进行归类,比如DDL、DML、DCL、查询语句、高级功能等,每个类别下给出具体的语句示例,并参考用户提供的引用中的例子。 例如,在DML部分,引用[1]中的INSERT INTO语句可以作为插入数据的示例。而在查询语句部分,引用[2]中的复杂联表查询可以作为多表联查的示例。需要确保这些例子被正确归类到思维导图的相应部分。 同时,用户可能希望思维导图的结构清晰,便于理解记忆。因此,需要将语句分类明确,每个类别下细分不同的语句类型,并附上语法示例说明。此外,用户可能对性能优化实际应用场景感兴趣,所以需要包含索引优化、事务控制等内容,如引用[2]中的索引使用事务处理。 另外,用户提到的引用[3]虽然Vagrant相关,但可能与当前需求无关,可能是一个错误引用,可以忽略,或者理解为用户可能对安装配置也有兴趣,但根据当前问题,应专注于语句本身。 最后,需要确保回答符合之前的格式要求,使用中文,正确应用引用标记,并在相关问题部分提出与MySQL语句相关的问题,帮助用户进一步深入学习。需要检查是否有遗漏的常见语句类别,确保思维导图全面覆盖常用MySQL语句,并结合用户提供的示例进行补充。</think>以下是针对**MySQL语句**的详细思维导图分类与说明,结合用户需求与引用示例进行结构化整理: --- ### MySQL语句思维导图 #### 1. **数据定义语言 (DDL)** - **创建对象** - 创建数据库:`CREATE DATABASE db_name;` - 创建表: ```sql CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, email VARCHAR(100) UNIQUE ); ``` - 创建索引:`CREATE INDEX idx_name ON users(name);` [^4] - **修改对象** - 添加列:`ALTER TABLE users ADD COLUMN age INT;` - 修改列类型:`ALTER TABLE users MODIFY COLUMN email VARCHAR(150);` [^2] - **删除对象** - 删除表:`DROP TABLE users;` - 删除数据库:`DROP DATABASE db_name;` --- #### 2. **数据操作语言 (DML)** - **插入数据** - 单行插入:`INSERT INTO users (name, email) VALUES ('张三', 'zhangsan@example.com');` - 批量插入(引用示例): ```sql INSERT INTO service VALUES (1,'192.168.16.13', 'mysql', 3306), (2,'192.168.16.13', 'kugou', 8080), (3,'192.168.16.13', 'flask', 5000); -- 来自引用[1] ``` - **更新数据** - 条件更新:`UPDATE users SET email = 'new@example.com' WHERE id = 1;` - **删除数据** - 条件删除:`DELETE FROM users WHERE age > 30;` --- #### 3. **数据查询语言 (DQL)** - **基础查询** - 查询所有列:`SELECT * FROM users;` - 指定列查询:`SELECT name, email FROM users;` - **条件过滤** - 简单条件:`SELECT * FROM users WHERE age >= 18;` - 多条件组合:`SELECT * FROM users WHERE age >= 18 AND email LIKE '%@example.com';` - **聚合与分组** - 计算平均值:`SELECT AVG(age) FROM users;` - 分组统计(引用示例): ```sql SELECT t1.university, t3.difficult_level, COUNT(t2.question_id) / COUNT(DISTINCT(t2.device_id)) AS avg_answer_cnt FROM user_profile AS t1, question_practice_detail AS t2, question_detail AS t3 WHERE t1.university='山东大学' AND t1.device_id = t2.device_id AND t2.question_id = t3.question_id GROUP BY t3.difficult_level; -- 来自引用[2] ``` - **多表联查** - 内连接: ```sql SELECT u.name, o.order_id FROM users u JOIN orders o ON u.id = o.user_id; ``` --- #### 4. **数据控制语言 (DCL)** - **权限管理** - 授予权限:`GRANT SELECT, INSERT ON db_name.* TO 'user'@'localhost';` - 撤销权限:`REVOKE DELETE ON db_name.* FROM 'user'@'localhost';` - **事务控制** - 开启事务:`START TRANSACTION;` - 提交事务:`COMMIT;` - 回滚事务:`ROLLBACK;` --- #### 5. **高级功能语句** - **视图操作** - 创建视图:`CREATE VIEW user_emails AS SELECT name, email FROM users;` - **存储过程** - 定义存储过程: ```sql DELIMITER // CREATE PROCEDURE GetUser(IN userId INT) BEGIN SELECT * FROM users WHERE id = userId; END // DELIMITER ; ``` - **触发器** - 创建触发器(示例): ```sql CREATE TRIGGER before_insert_user BEFORE INSERT ON users FOR EACH ROW SET NEW.created_at = NOW(); ``` --- ### 附:性能优化相关语句 - **索引优化** - 分析查询计划:`EXPLAIN SELECT * FROM users WHERE name = '张三';` [^4] - **慢查询日志** - 启用慢查询日志: ```sql SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 2; -- 单位:秒 ``` --- ```python # 示例代码:创建带外键约束的表(与用户引用示例呼应) CREATE TABLE orders ( order_id INT PRIMARY KEY, user_id INT, amount DECIMAL(10,2), FOREIGN KEY (user_id) REFERENCES users(id) -- 外键关联 ); ``` --- ### 相关问题 1. **如何通过索引优化复杂联表查询的性能?** - 答案:通过为连接字段(如 `device_id`、`question_id`)添加索引,减少全表扫描 [^4]。 2. **MySQL事务中保存点(SAVEPOINT)的使用场景是什么?** - 答案:用于在事务内部分阶段回滚,例如处理批量数据时部分失败的情况。 3. **如何分析一条SQL语句的执行效率?** - 答案:使用 `EXPLAIN` 关键字解析查询计划,检查是否命中索引 [^4]。 --- 通过以上分类,您可以将MySQL语句按功能模块整理成思维导图,便于系统化学习快速查阅。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值