MySQL 学习笔记 2:触发器

news2024/10/7 12:20:26

MySQL 学习笔记 2:触发器

image-20230710222532579

图源:ubiq.co

触发器,就像字面意思那样,它会在数据库某些事件发生时执行一些操作。

具体来说,触发器会在特定表的INSERTUPDATEDELETE这些类型的 SQL 语句执行时被“触发”,并执行触发器中定义好的(一条或多条) SQL 语句。

在 MySQL 中,触发器存在一些限制,我们只能对同一张表定义最多6个触发器,分别对应6个事件:

  • BEFORE INSERT
  • AFTER INSERT
  • BEFORE UPDATE
  • AFTER UPDATE
  • BEFORE DELETE
  • AFTER DELETE

此外,无法在触发器中调用存储过程,也无法对临时表和视图使用存储过程。

本文使用的数据库可视化工具和测试数据同上一篇文章,不再赘述。

创建触发器

用 SQLyog 创建触发器会有类似下面这样的模版:

DELIMITER $$

CREATE
    /*[DEFINER = { user | CURRENT_USER }]*/
    TRIGGER `jpa`.`afterStudentAdd` BEFORE/AFTER INSERT/UPDATE/DELETE
    ON `jpa`.`<Table Name>`
    FOR EACH ROW BEGIN

    END$$

DELIMITER ;

这里的afterStudentAdd是我们的触发器名称,名称后需要指明执行触发器的事件,也就是前面说过的6种事件之一,这里我们要按需要修改。ON后是触发器关联的数据库和表,这里要填写需要触发器的表名。FOR EACH ROW说明每一条数据触发事件后都会执行触发器内的 SQL。最后,在BEGINEND之间填写触发器的 SQL,如果只有一条 SQL 要执行,BEGINEND可以省略。

原则上同一张表的触发器名称唯一即可,但作为良好习惯,触发器名称应该在数据库中唯一。

INSERT

下面是我编写的触发器:

DELIMITER $$

CREATE
    /*[DEFINER = { user | CURRENT_USER }]*/
    TRIGGER `jpa`.`afterStudentAdd`  AFTER INSERT
    ON `jpa`.`student`
    FOR EACH ROW BEGIN
        declare nowTime datetime default NOW();
        INSERT INTO `jpa`.`student_add_log` (`id`, `average_score`, `level`, `name`, `time`) VALUES (NEW.id, NEW.average_score, NEW.level, NEW.name, nowTime);
    END$$

DELIMITER ;

这个触发器将在INSERT语句执行后执行,作用是将新添加的数据插入一个日志表student_add_log,并且记录添加时间。在 INSERT 事件中,我们可以使用临时表NEW获取新添加的数据。需要注意的是,自增主键只有在数据真正添加后(INSERT语句执行后)才会产生,所以类似的NEW.id只有在AFTER INSERT事件中才能获取到,BEFORE INSERT中是不存在的。

实际上这里的局部变量nowTime并不是必须的。

日志表的表结构如下:

CREATE TABLE `student_add_log` (
  `id` bigint NOT NULL,
  `average_score` int NOT NULL,
  `level` enum('FRESH_MAN','JUNIOR','SENIOR','SOPHOMORE') NOT NULL,
  `name` varchar(45) NOT NULL,
  `time` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

需要注意的是,**如果触发器关联的事件是BEFORE INSERT,而且触发器执行失败,那后续的INSERT语句和AFTER INSERT关联的触发器就不会再执行。**这点其它的BEFORE XXX触发器也是同样的。

我们还可以利用BEFORE INSERT事件,在执行插入语句之前对要插入的数据进行检查,或者将某些数据处理成我们需要的格式:

DELIMITER $$

CREATE
    /*[DEFINER = { user | CURRENT_USER }]*/
    TRIGGER `jpa`.`beforeStudentAdd` BEFORE INSERT
    ON `jpa`.`student`
    FOR EACH ROW BEGIN
	set NEW.name = INSERT(NEW.name,1,1,UCASE(LEFT(NEW.name,1)));
    END$$

DELIMITER ;

现在即使 INSERT SQL 中要插入的name字段的值首字母小写,也会在执行 SQL 时被触发器替换为首字母大写的形式,这样就确保了数据库中student表的name字段首字母都是大写。

关于 MySQL 的相关函数说明,可以阅读这里。

修改 & 删除触发器

用 SQLyog 修改触发器会提供类似下面的模版 SQL:

DELIMITER $$

USE `jpa`$$

DROP TRIGGER /*!50032 IF EXISTS */ `afterStudentAdd`$$

CREATE
    /*!50017 DEFINER = 'root'@'localhost' */
    TRIGGER `afterStudentAdd` AFTER INSERT ON `student` 
    FOR EACH ROW BEGIN
	DECLARE nowTime DATETIME DEFAULT NOW();
	INSERT INTO `jpa`.`student_add_log` (`id`, `average_score`, `level`, `name`, `time`) VALUES (NEW.id, NEW.average_score, NEW.level, NEW.name, nowTime); 
    END;
$$

DELIMITER ;

和存储过程类似,触发器只能在删除后重新添加,删除触发器的语句是:

DROP TRIGGER `afterStudentAdd`;

如果触发器不存在时避免报错,可以:

DROP TRIGGER IF EXISTS `afterStudentAdd`;

UPDATE

下面是我编写的一个在BEFORE UPDATE事件发生时执行的触发器:

DELIMITER $$

CREATE
    /*[DEFINER = { user | CURRENT_USER }]*/
    TRIGGER `jpa`.`beforeStudentUpdate` BEFORE UPDATE
    ON `jpa`.`student`
    FOR EACH ROW BEGIN
	INSERT INTO `jpa`.`student_update_log` (`old_id`, `old_average_score`, `old_level`, `old_name`, `new_id`, `new_average_score`, `new_level`, `new_name`, `time`) VALUES (OLD.id, OLD.average_score, OLD.level, OLD.name, NEW.id, NEW.average_score, NEW.level, NEW.name, NOW()); 
    END$$

DELIMITER ;

这个触发器会在student中的表数据修改时将其记录到日志表。

UPDATE事件中,可以用临时表NEW访问修改后的表数据,可以用临时表OLD访问被修改前的表数据。

日志表结构如下:

CREATE TABLE `student_update_log` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `old_id` bigint NOT NULL,
  `old_average_score` int NOT NULL,
  `old_level` enum('FRESH_MAN','JUNIOR','SENIOR','SOPHOMORE') NOT NULL,
  `old_name` varchar(45) NOT NULL,
  `new_id` bigint NOT NULL,
  `new_average_score` int NOT NULL,
  `new_level` enum('FRESH_MAN','JUNIOR','SENIOR','SOPHOMORE') NOT NULL,
  `new_name` varchar(45) NOT NULL,
  `time` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

这里使用BEFORE UPDATE而不是AFTER UPDATE的好处在于,可以保证任何情况下都能记录日志信息(即使UPDATE语句执行失败)。当然这也可能是缺陷,要具体问题具体分析。

BEFORE INSERT类似,在 BEFORE UPDATE 事件关联的触发器中,我们也可以修改临时表NEW中的值,这样就会导致之后执行的INSERT语句使用我们修改后的值来进行插入:

DELIMITER $$

USE `jpa`$$

DROP TRIGGER /*!50032 IF EXISTS */ `beforeStudentUpdate`$$

CREATE
    /*!50017 DEFINER = 'root'@'localhost' */
    TRIGGER `beforeStudentUpdate` BEFORE UPDATE ON `student` 
    FOR EACH ROW BEGIN
	set NEW.name = INSERT(NEW.name,1,1,UCASE(LEFT(NEW.name,1)));
	INSERT INTO `jpa`.`student_update_log` (`old_id`, `old_average_score`, `old_level`, `old_name`, `new_id`, `new_average_score`, `new_level`, `new_name`, `time`) VALUES (OLD.id, OLD.average_score, OLD.level, OLD.name, NEW.id, NEW.average_score, NEW.level, NEW.name, NOW()); 
    END;
$$

DELIMITER ;

这里修改了存储过程beforeStudentUpdate,通过以下命令改写了临时表NEW中的name字段:

set NEW.name = INSERT(NEW.name,1,1,UCASE(LEFT(NEW.name,1)));

这个命令的用途是将name字段值的首字母替换为大写。

DELETE

最后,用类似的方式添加一个触发器,用于在执行删除 SQL 时将删除的数据保存到日志表:

DELIMITER $$

CREATE
    /*[DEFINER = { user | CURRENT_USER }]*/
    TRIGGER `jpa`.`beforeStudentDelete` BEFORE DELETE
    ON `jpa`.`student`
    FOR EACH ROW BEGIN
	INSERT INTO `jpa`.`student_delete_log` (`id`, `average_score`, `level`, `name`, `time`) VALUES (OLD.id, OLD.average_score, OLD.level, OLD.name, NOW()); 
    END$$

DELIMITER ;

DELETE事件时,可以用临时表OLD获取被删除的旧数据。

日志表结构:

CREATE TABLE `student_delete_log` (
  `id` bigint NOT NULL,
  `average_score` int NOT NULL,
  `level` enum('FRESH_MAN','JUNIOR','SENIOR','SOPHOMORE') NOT NULL,
  `name` varchar(45) NOT NULL,
  `time` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

总结

触发器的优点在于:在数据库端是透明的,可以通过设置触发器来控制数据录入或者记录日志信息。这些都不受客户端调用的影响,无论 SQL 是以什么样的方式在数据库上执行,相应的触发器都会起作用。

缺点在于:这些触发器对于代码端是“隐藏的”,只凭借代码是无法察觉到这些触发器的,是容易被忽略的。

说到底,触发器属于数据库编程的部分,对于开发人员有一定要求。

The End,谢谢阅读。

参考资料

  • 《MySQL 必知必会》
  • MySQL 函数 | 菜鸟教程 (runoob.com)

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

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

相关文章

CopyOnWriteArrayList使用以及原理分析

文章目录 一、CopyOnWriteArrayList的简介二、CopyOnWriteArrayList类的继承关系1、Iterable接口&#xff1a;2、Collection接口&#xff1a;3、List接口&#xff1a;4、Cloneable接口&#xff1a;5、Serializable接口&#xff1a;6、RandomAccess接口&#xff1a; 三、CopyOnW…

模糊图片怎么修复清晰度?这几个方法分享给你~

在我们的日常生活和工作中&#xff0c;经常会遇到图片模糊的问题&#xff0c;这可能是由于拍摄时的手抖、对焦不准确或者图片压缩过度等原因造成的。那么&#xff0c;如何修复模糊的图片&#xff0c;提高其清晰度呢&#xff1f;本文将为您介绍几种方法。 方法一&#xff1a;使…

ABAP调用阿里云接口-短信服务-HTTP协议及签名(abap版本)<转载>

原文链接&#xff1a;https://blog.csdn.net/xiefireworks/article/details/113037650 阿里云接口文档请参考官网地址 https://help.aliyun.com/document_detail/59210.html?spm5176.8195934.J_5834642020.5.11ba4378DLVi4O 此处仅介绍使用ABAP完成阿里云短信服务签名请求的…

树莓派4B的串口UART配置

1 安装串口&#xff1a; 如果没有更换pip源会报错&#xff0c;所以指定安装源 pip install pyserial -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com 修改uart配置&#xff1a; vim /boot/firmware/config.txt 在末尾添加&#xff1a; dtoverlayuart2…

X86架构的Linux(Ubuntu版本)上离线安装CUnit来解决Could not find CUnit(missing:CUNIT_LIBRARY)问题

前言1 下载cunit压缩安装包&#xff1a;CUint-2.1-3.tar.bz2&#xff08;为了安装成功请下载对应版本&#xff09;2 解压安装压缩包3 sudo ./bootstrap --prefix/usr/local/cunit 生成可执行文件configure*4 sudo ./configure --prefix/usr/local/cunit5 sudo make . 编译 &…

Centos安装指定docker版本和docker-compose

目录 一. 直接安装Docker最新镜像源 1. 卸载旧版本的Docker&#xff1a; 2. 安装依赖包&#xff1a; 3. 添加Docker源&#xff1a; 4. 安装Docker&#xff1a; 5. 启动Docker服务&#xff1a; 6. 验证Docker是否安装成功&#xff1a; 二、指定Docker版本安装 1. 查看…

已解决‘mongo‘ 不是内部或外部命令,也不是可运行的程序

已解决&#xff08;MongoDB安装报错&#xff09;‘mongo’ 不是内部或外部命令,也不是可运行的程序 报错代码 粉丝群里的一个小伙伴安装完MongoDB后&#xff0c;在cmd中启动&#xff0c;却说不是可运行的命令&#xff1f; 报错原因 报错原因&#xff1a;由于没有配置环境变量的…

【Redis】4、全局唯一 ID生成、单机(非分布式)情况下的秒杀和一人一单

目录 一、利用 Redis 实现全局唯一 ID 生成(1) 为啥要用全局唯一 ID 生成(2) 全局唯一 ID 生成器(3) 全局 ID 的结构(4) 代码实现① RedisIdWorker② Test (5) 全局唯一 ID 其他生成策略 二、添加优惠券(1) 数据库(2) 添加优惠券接口 三、优惠券秒杀下单功能(1) 超卖问题(2) 乐…

项目上线“G”速报 | GBASE助力四川银行反洗钱系统上线运行

随着金融机构资管业务的不断发展&#xff0c;藉由以银行为代表的金融机构建设反洗钱系统&#xff0c;向执法机构报送可疑活动&#xff0c;成为侦测潜在金融犯罪、打击腐败的重要防线。为更好助力反洗钱工作&#xff0c;四川银行着手构建新一代的反洗钱系统。作为信创第二期的重…

重磅预告丨Fortinet Demo Day系列实战攻防演练来袭!

随着网络安全形势的日趋严峻&#xff0c;越来越多的企业遭受了勒索、欺诈等危害。在高昂的赎金、生产损失&#xff0c;以及名誉损害的恐惧中&#xff0c;企业已经谈“黑”色变。黑客是如何悄无声息的“越过”重重高墙、道道壁垒进入到生产环境、办公空间&#xff0c;并在内网疯…

Android各种支持裤的最新依赖以及用户文档

https://developer.android.com/jetpack/androidx/versions 链接截图如下&#xff1a; 点击“Release Notes”中的链接&#xff0c;如果对应支持库有用户指南还能看到对应链接&#xff0c;还有如何添加依赖等&#xff0c;比如支持库中的actviity&#xff0c;如下&#xff1a;截…

【漂移-扩散通量重建 FV 方案】用于半导体和气体放电模拟的电子传输的更准确的 Sharfetter-Gummel 算法(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Perl 7 - 使用 Perlbrew 管理perl 版本

文章目录 关于 Perlbrew安装 Perlbrew使用 perlbrew 安装/管理 perl 版本 关于 Perlbrew 官网&#xff1a;https://perlbrew.pl 相关文档&#xff1a; App::perlbrew https://metacpan.org/pod/App::perlbrew Perlbrew 是一个工具&#xff0c;用于管理您$HOME 目录(或您指定的…

Flink基于信用值的流量控制

背景 flink内部实现了一个类似于tcp滑动窗口概念的流量控制功能&#xff0c;以满足其内部的流量控制功能&#xff0c;本文就来讲解下flink实现的基于信用值的流量控制的原理 实现原理 首先&#xff0c;我们先来看一下在flink中是如何实现数据传输的&#xff0c; 从上图可知&…

css animation 鼠标移入暂停会抖动

如图 实现一个赞助商横向滚动列表墙&#xff0c; 上下两排向右滚动&#xff0c;中间向左滚动&#xff0c;鼠标移入暂停当前行。 实现&#xff1a; // 使用animation.moving {animation: move 20s linear infinite; }keyframes move {0% {}100% {transform: translateX(-50%);…

可靠的手机问题修复工具分享 - 修复各种 Android 系统问题

一般来说&#xff0c;安卓手机都可以流畅运行。但不幸的是&#xff0c;有时您的Android手机可能无法正常运行&#xff0c;例如无响应、突然重启等。在这种情况下&#xff0c;您将需要Android手机维修软件。这些 Android 修复工具可以帮助您轻松解决此类问题&#xff0c;并还给您…

QEMU源码全解析4 —— QEMU参数解析(4)

接前一篇文章&#xff1a;QEMU源码全解析3 —— QEMU参数解析&#xff08;3&#xff09; 本文内容参考&#xff1a; 《趣谈Linux操作系统》 —— 刘超&#xff0c;极客时间 《QEMU/KVM》源码解析与应用 —— 李强&#xff0c;机械工业出版社 特此致谢&#xff01; 本回讲解Q…

JavaWeb学生考勤签到请假系统

一、项目简介 本项目是一套JavaWeb学生考勤签到请假系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;ecl…

生成图片验证码-Google Kaptcha

CaptchaImage生成 验证码 图片 captchaProducerMath.createText() 类似 captchaProducer.createText() 混合带字符的char如下 从若依学的&#xff0c;先看他的引用方式 package com.ruoyi.web.controller.common;import java.awt.image.BufferedImage; import java.io.IOExcept…

【导航算路(RP)模块功能】

什么是RP Route Production/Route Planning 就是在给定自车位置和目的地的情况下&#xff0c;按照用户设定的不同条件&#xff0c;计算出一条或多条从自车位置到目的地的花费(根据用户的设定&#xff0c;可能是指时间&#xff0c;费用等)最少的最优路以供用户使用。 为什么要…