数据库基础篇 《17.触发器》

news2024/12/28 5:04:17

数据库基础篇 《17.触发器》

在实际开发中,我们经常会遇到这样的情况:有 2 个或者多个相互关联的表,如商品信息库存信息分别存放在 2 个不同的数据表中,我们在添加一条新商品记录的时候,为了保证数据的完整性,必须同时在库存表中添加一条库存记录。

这样一来,我们就必须把这两个关联的操作步骤写到程序里面,而且要用事务包裹起来,确保这两个操作成为一个原子操作,要么全部执行,要么全部不执行。要是遇到特殊情况,可能还需要对数据进行手动维护,这样就很容易忘记其中的一步,导致数据缺失。

这个时候,咱们可以使用触发器。**你可以创建一个触发器,让商品信息数据的插入操作自动触发库存数据的插入操作。**这样一来,就不用担心因为忘记添加库存数据而导致的数据缺失了。

1. 触发器概述

MySQL从5.0.2版本开始支持触发器。MySQL的触发器和存储过程一样,都是嵌入到MySQL服务器的一段程序。

触发器是由事件来触发某个操作,这些事件包括INSERTUPDATEDELETE事件。所谓事件就是指用户的动作或者触发某项行为。如果定义了触发程序,当数据库执行这些语句时候,就相当于事件发生了,就会自动激发触发器执行相应的操作。

当对数据表中的数据执行插入、更新和删除操作,需要自动执行一些数据库逻辑时,可以使用触发器来实现。

2. 触发器的创建

2.1 创建触发器语法

创建触发器的语法结构是:

CREATE TRIGGER 触发器名称 
{BEFORE|AFTER} {INSERT|UPDATE|DELETE} ON 表名 
FOR EACH ROW 
触发器执行的语句块;

说明:

  • 表名:表示触发器监控的对象。

  • BEFORE|AFTER:表示触发的时间。BEFORE 表示在事件之前触发;AFTER 表示在事件之后触发。

  • INSERT|UPDATE|DELETE:表示触发的事件。

    • INSERT 表示插入记录时触发;
    • UPDATE 表示更新记录时触发;
    • DELETE 表示删除记录时触发。
  • 触发器执行的语句块:可以是单条SQL语句,也可以是由BEGIN…END结构组成的复合语句块。

2.2 代码举例

举例1:

1、创建数据表:

CREATE TABLE test_trigger (
id INT PRIMARY KEY AUTO_INCREMENT,
t_note VARCHAR(30)
);


CREATE TABLE test_trigger_log (
id INT PRIMARY KEY AUTO_INCREMENT,
t_log VARCHAR(30)
);

2、创建触发器:创建名称为before_insert的触发器,向test_trigger数据表插入数据之前,向test_trigger_log数据表中插入before_insert的日志信息。

DELIMITER //

CREATE TRIGGER before_insert
BEFORE INSERT ON test_trigger 
FOR EACH ROW
BEGIN
	INSERT INTO test_trigger_log (t_log)
	VALUES('before_insert');

END //

DELIMITER ;

3、向test_trigger数据表中插入数据

INSERT INTO test_trigger (t_note) VALUES ('测试 BEFORE INSERT 触发器');

4、查看test_trigger_log数据表中的数据

mysql> SELECT * FROM test_trigger_log;
+----+---------------+
| id | t_log         |
+----+---------------+
|  1 | before_insert |
+----+---------------+
1 row in set (0.00 sec)

举例2:

1、创建名称为after_insert的触发器,向test_trigger数据表插入数据之后,向test_trigger_log数据表中插入after_insert的日志信息。

DELIMITER //

CREATE TRIGGER after_insert
AFTER INSERT ON test_trigger
FOR EACH ROW
BEGIN
	INSERT INTO test_trigger_log (t_log)
	VALUES('after_insert');
END //

DELIMITER ;

2、向test_trigger数据表中插入数据。

INSERT INTO test_trigger (t_note) VALUES ('测试 AFTER INSERT 触发器');

3、查看test_trigger_log数据表中的数据

mysql> SELECT * FROM test_trigger_log;
+----+---------------+
| id | t_log         |
+----+---------------+
|  1 | before_insert |
|  2 | before_insert |
|  3 | after_insert  |
+----+---------------+
3 rows in set (0.00 sec)

**举例3:**定义触发器“salary_check_trigger”,基于员工表“employees”的INSERT事件,在INSERT之前检查将要添加的新员工薪资是否大于他领导的薪资,如果大于领导薪资,则报sqlstate_value为’HY000’的错误,从而使得添加失败。

DELIMITER //

CREATE TRIGGER salary_check_trigger
BEFORE INSERT ON employees FOR EACH ROW
BEGIN
	DECLARE mgrsalary DOUBLE;
	SELECT salary INTO mgrsalary FROM employees WHERE employee_id = NEW.manager_id;

	IF NEW.salary > mgrsalary THEN
		SIGNAL SQLSTATE 'HY000' SET MESSAGE_TEXT = '薪资高于领导薪资错误';
	END IF;
END //

DELIMITER ;

上面触发器声明过程中的NEW关键字代表INSERT添加语句的新记录。

3. 查看、删除触发器

3.1 查看触发器

查看触发器是查看数据库中已经存在的触发器的定义、状态和语法信息等。

方式1:查看当前数据库的所有触发器的定义

SHOW TRIGGERS\G

方式2:查看当前数据库中某个触发器的定义

SHOW CREATE TRIGGER 触发器名

方式3:从系统库information_schema的TRIGGERS表中查询“salary_check_trigger”触发器的信息。

SELECT * FROM information_schema.TRIGGERS;

3.2 删除触发器

触发器也是数据库对象,删除触发器也用DROP语句,语法格式如下:

DROP TRIGGER  IF EXISTS 触发器名称;

4. 触发器的优缺点

4.1 优点

1、触发器可以确保数据的完整性

假设我们用进货单头表(demo.importhead)来保存进货单的总体信息,包括进货单编号、供货商编号、仓库编号、总计进货数量、总计进货金额和验收日期。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y1dSOUux-1682450111129)(images/image-20211010233336012.png)]

进货单明细表(demo.importdetails)来保存进货商品的明细,包括进货单编号、商品编号、进货数量、进货价格和进货金额。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6h6XPnWa-1682450111130)(images/image-20211010233344125.png)]

每当我们录入、删除和修改一条进货单明细数据的时候,进货单明细表里的数据就会发生变动。这个时候,在进货单头表中的总计数量和总计金额就必须重新计算,否则,进货单头表中的总计数量和总计金额就不等于进货单明细表中数量合计和金额合计了,这就是数据不一致。

为了解决这个问题,我们就可以使用触发器,规定每当进货单明细表有数据插入、修改和删除的操作时,自动触发 2 步操作:

1)重新计算进货单明细表中的数量合计和金额合计;

2)用第一步中计算出来的值更新进货单头表中的合计数量与合计金额。

这样一来,进货单头表中的合计数量与合计金额的值,就始终与进货单明细表中计算出来的合计数量与合计金额的值相同,数据就是一致的,不会互相矛盾。

2、触发器可以帮助我们记录操作日志。

利用触发器,可以具体记录什么时间发生了什么。比如,记录修改会员储值金额的触发器,就是一个很好的例子。这对我们还原操作执行时的具体场景,更好地定位问题原因很有帮助。

3、触发器还可以用在操作数据前,对数据进行合法性检查。

比如,超市进货的时候,需要库管录入进货价格。但是,人为操作很容易犯错误,比如说在录入数量的时候,把条形码扫进去了;录入金额的时候,看串了行,录入的价格远超售价,导致账面上的巨亏……这些都可以通过触发器,在实际插入或者更新操作之前,对相应的数据进行检查,及时提示错误,防止错误数据进入系统。

4.2 缺点

1、触发器最大的一个问题就是可读性差。

因为触发器存储在数据库中,并且由事件驱动,这就意味着触发器有可能不受应用层的控制。这对系统维护是非常有挑战的。

比如,创建触发器用于修改会员储值操作。如果触发器中的操作出了问题,会导致会员储值金额更新失败。我用下面的代码演示一下:

mysql> update demo.membermaster set memberdeposit=20 where memberid = 2;
ERROR 1054 (42S22): Unknown column 'aa' in 'field list'

结果显示,系统提示错误,字段“aa”不存在。

这是因为,触发器中的数据插入操作多了一个字段,系统提示错误。可是,如果你不了解这个触发器,很可能会认为是更新语句本身的问题,或者是会员信息表的结构出了问题。说不定你还会给会员信息表添加一个叫“aa”的字段,试图解决这个问题,结果只能是白费力。

2、相关数据的变更,可能会导致触发器出错。

特别是数据表结构的变更,都可能会导致触发器出错,进而影响数据操作的正常运行。这些都会由于触发器本身的隐蔽性,影响到应用中错误原因排查的效率。

4.3 注意点

注意,如果在子表中定义了外键约束,并且外键指定了ON UPDATE/DELETE CASCADE/SET NULL子句,此时修改父表被引用的键值或删除父表被引用的记录行时,也会引起子表的修改和删除操作,此时基于子表的UPDATE和DELETE语句定义的触发器并不会被激活。

例如:基于子表员工表(t_employee)的DELETE语句定义了触发器t1,而子表的部门编号(did)字段定义了外键约束引用了父表部门表(t_department)的主键列部门编号(did),并且该外键加了“ON DELETE SET NULL”子句,那么如果此时删除父表部门表(t_department)在子表员工表(t_employee)有匹配记录的部门记录时,会引起子表员工表(t_employee)匹配记录的部门编号(did)修改为NULL,但是此时不会激活触发器t1。只有直接对子表员工表(t_employee)执行DELETE语句时才会激活触发器t1。

4. 触发器的优缺点课后练习

练习1:

0. 准备工作

CREATE DATABASE test17_trigger;

USE test17_trigger;

CREATE TABLE emps
AS
SELECT employee_id,last_name,salary
FROM atguigudb.`employees`;

SELECT * FROM emps;

1. 复制一张emps表的空表emps_back,只有表结构,不包含任何数据

CREATE TABLE emps_back
AS
SELECT * FROM emps
WHERE 1 = 2;

2. 查询emps_back表中的数据

SELECT * FROM emps_back;

3. 创建触发器emps_insert_trigger,每当向emps表中添加一条记录时,同步将这条记录

#添加到emps_back表中

DELIMITER //

CREATE TRIGGER emps_insert_trigger
AFTER INSERT ON emps
FOR EACH ROW
BEGIN
	#将新添加到emps表中的记录添加到emps_back表中
	INSERT INTO emps_back(employee_id,last_name,salary)
	VALUES(NEW.employee_id,NEW.last_name,NEW.salary);
END //

DELIMITER ;

#show triggers;

4. 验证触发器是否起作用

SELECT * FROM emps;

SELECT * FROM emps_back;

INSERT INTO emps(employee_id,last_name,salary)
VALUES(301,'Tom1',3600);

练习2:

#0. 准备工作:使用练习1中的emps表

#1. 复制一张emps表的空表emps_back1,只有表结构,不包含任何数据

CREATE TABLE emps_back1
AS
SELECT * 
FROM emps
WHERE 1 = 2;

#2. 查询emps_back1表中的数据

SELECT * FROM emps_back1;

3. 创建触发器emps_del_trigger,每当向emps表中删除一条记录时,同步将删除的这条记录添加到emps_back1表中

DELIMITER //

CREATE TRIGGER emps_del_trigger
BEFORE DELETE ON emps
FOR EACH ROW
BEGIN
	#将emps表中删除的记录,添加到emps_back1表中。
	INSERT INTO emps_back1(employee_id,last_name,salary)
	VALUES(OLD.employee_id,OLD.last_name,OLD.salary);
END //

DELIMITER ;

4. 验证触发器是否起作用

DELETE FROM emps
WHERE employee_id = 101;


DELETE FROM emps;

SELECT * FROM emps;


SELECT * FROM emps_back1;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/462883.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【刷题之路】LeetCode 203. 移除链表元素

【刷题之路】LeetCode 203. 移除链表元素 一、题目描述二、解题1、方法1——在原链表上动刀子1.1、思路分析1.2、代码实现 2、方法2——使用额外的链表2.1、思路分析2.2、代码实现 一、题目描述 原题连接: 203. 移除链表元素 题目描述: 给你一个链表的…

跨数据中心下的 Kafka 高可用架构分析

导语 本文介绍了 Kafka 跨数据中心的两种部署方式,简要分析两种方式下的不同架构以及优缺点,对这些架构可能碰到的问题也提供了一些解决思路;同时也说明了 Kafka 跨数据中心部署的社区解决方案和商业化解决方案。 背景 Kafka 作为世界上最…

laravel5.6.* + vue2 创建后台

本地已经安装好了composer 1.新建 Laravel5.6.*项目 composer create-project --prefer-dist laravel/laravel laravel5vue2demo 5.6.* 2. cd laravel5vue2demo 3. npm install /routes/web.php 路由文件中, 修改 Route::get(/, function () {return view(index); });新建…

第三方jar包引入项目,发布到本地和远程仓库

在开发过程中,往往会遇到对接其他公司的系统。然后对接公司会提供API对接方式,就是给一个jar包。我们只需要把jar包引入到项目中直接用即可。本地引用jar的话可以有两种方式。第一种就是本地包引用,如下将包放下工程下,然后maven指…

【五一劳动节来了】

今年“五一”,4月29日至5月3日放假调休,共5天。 如果你在5月4日到5月6日请假3天,加上5月7日周日,就可以形成9天的假期。 一,五一劳动节的由来⭐ 国际劳动节又称“五一国际劳动节”“国际示威游行日”(英语…

抢先看,甘特图工具DHTMLX gantt 灯箱编辑器通过套件 UI 小部件进行了扩展

DHTMLX Gantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表。可满足项目管理应用程序的大部分开发需求,具备完善的甘特图图表库,功能强大,价格便宜,提供丰富而灵活的JavaScript API接口,与各种服务器端技术&am…

【STL十四】函数对象(function object)_仿函数(functor)——lambda表达式

【STL十四】函数对象(function object)_仿函数(functor)——lambda表达式 一、函数对象(function object)二、函数对象优点三、分类四、头文件五、用户定义函数对象demo六、std::内建函数对象1、 算术运算函…

YARN 远程代码执行(RCE)安全漏洞问题分析与解决方案

YARN 远程代码执行(RCE)安全漏洞问题分析与解决方案 1 YARN RCE 漏洞问题问题现象 某客户使用Tenable.sc扫描安全漏洞后反馈,YARN 存在Remote code execution (RCE) 安全漏洞问题,攻击者可在未经过身份验证的情况下通过该漏洞在…

【21】核心易中期刊推荐——人工智能 | 遥感图像识别

🚀🚀🚀NEW!!!核心易中期刊推荐栏目来啦 ~ 📚🍀 核心期刊在国内的应用范围非常广,核心期刊发表论文是国内很多作者晋升的硬性要求,并且在国内属于顶尖论文发表,具有很高的学术价值。在中文核心目录体系中,权威代表有CSSCI、CSCD和北大核心。其中,中文期刊的数…

psql在建表时,分为常规、外部、分区,三者有什么区别?如何从建表语句中区分?

在 PostgreSQL 中,常规表、外部表和分区表都可以通过 CREATE TABLE 语句进行创建,它们的创建语法略有不同,通过创建语句可以很明显地区分它们的类型。 以下是常规表、外部表和分区表的创建语法及示例: 1. 常规表 常规表是最常见…

Spring核心与设计思想、创建与使用

文章目录 一、Spring是什么二、为什么要学习框架三、IoC和DI(一)IoC1. 认识IoC2. Spring的核心功能 (二)DI 四、Spring项目的创建(一)使用 Maven 方式创建一个 Spring 项目 五、Spring项目的使用&#xff0…

少年与阿童木:一场软件竞技赛背后的智能未来

1961年,手冢治虫创办了虫制作株式会社,带领团队开始尝试将此前的漫画作品进行动画化。1963年的元旦,他们的首部作品一经播出就引发轰动,这部动画的名字叫做——《铁臂阿童木》。 一晃数十年,阿童木已经成为了几代人对A…

2023年6月DAMA-CDGA/CDGP数据治理工程师认证报名及费用

目前6月DAMA-CDGA/CDGP数据治理认证考试开放报名地区有:北京、上海、广州、深圳、长沙、呼和浩特。目前南京、济南、西安、杭州等地区还在接近开考人数中,打算6月考试的朋友们可以抓紧时间报名啦!!! 5月初,…

大数据 | 实验二:文档倒排索引算法实现

文章目录 📚实验目的📚实验平台📚实验内容🐇在本地编写程序和调试🥕代码框架思路🥕代码实现 🐇在集群上提交作业并执行🥕在集群上提交作业并执行,同本地执行相比即需修改…

蓝牙耳机怎么挑选?鹏鹏数码盘点2023口碑蓝牙耳机排行榜

大家好,欢迎来到鹏鹏数码频道。 上次测评发布后网友们评论不知道蓝牙耳机怎么挑选,为此我购入了市面上主流品牌的蓝牙耳机共计三十款, 经过两周的地狱式测评,总结了口碑蓝牙耳机排行榜,看看表现最好的是哪几款蓝牙耳机…

Linux操作系统命令大全

Linux是一种操作系统 Operating System 简称 OS ,是软件的一部分,它是硬件基础上的第一层软件,是硬件和其它软件沟通的桥梁。 操作系统会控制其他程序运行,管理系统资源,提供最基本的计算功能,如管理及配置…

SSM整合(一) | SSM创建项目配置整合 - 添加功能模块

文章目录 SSM整合SSM配置整合SSM功能模块 SSM整合 SSM配置整合 SSM整合流程: 创建工程SSM整合 Spring SpringConfig MyBatis MybatisConfigJdbcConfigjdbc.properties SpringMVC ServletConfigSpringMvcConfig 创建工程 基于Maven创建项目, 选择webapp模版并补全缺失的目录 …

最优化方法Python计算:一元函数导数的数值计算

定义1 给定连续函数 f ( x ) f(x) f(x), x ∈ Ω ⊆ R x\in\Omega\subseteq\text{ℝ} x∈Ω⊆R。设 x , x 1 ∈ Ω x,x_1\in\Omega x,x1​∈Ω, Δ x x − x 1 \Delta xx-x_1 Δxx−x1​ 称为变量 x x x的差分。此时, x x 1 Δ x xx_1\De…

黑盒测试过程中【测试方法】详解5-输入域,输出域,猜错法

在黑盒测试过程中,有9种常用的方法:1.等价类划分 2.边界值分析 3.判定表法 4.正交实验法 5.流程图分析 6.因果图法 7.输入域覆盖法 8.输出域覆盖法 9.猜错法 黑盒测试过程中【测试方法】讲解1-等价类,边界值,判定表_朝一…

Linux下实现共享内存的两种机制(附源码)

START Hello 大家好。 今天来讲一下Linux进程通信中重要的通信方式:共享内存作为Linux软件开发攻城狮,进程间通信是必须熟练掌握的重要技能,而共享内存是在程序开发中常用的也是重要的一种进程间通信方式。 下面我们就来聊一聊Linux下进程间…