当前位置: 首页 > news >正文

[mysql]游标和触发器

目录

游标(或光标)

定义

使用过程

示例

总结

触发器

应用场景

定义

使用

创建

查看

删除

示例

一个注意点:

优缺点

拓展:MySQL 8.0的新特性—全局变量的持久化


游标(或光标)

定义

游标是一种 能够对结果集中的每一条记录进行定位(像指针一样,向前定位一条记录、向后定位一条记录,或者是 随意定位到某一条记录 ),并对指向的记录中的数据进行操作的数据结构。

游标让 SQL这种面向集合的语言有了面向过程开发的能力。

特点

  • 在 SQL 中,游标是一种临时的数据库对象,可以指向存储在数据库表中的数据行指针。

这里游标充当指针的作用,通过操作游标来对数据行进行操作.

  • 游标必须在声明处理程序之前被声明

相对的,变量和条件则必须在声明游标和处理程序之前被声明。

使用过程

使用游标一般分4步,不同DBMS细节不同。

  1. 声明游标

    • mysql(还有SQL Server,DB2 和 MariaDB)

       DECLARE cursor_name CURSOR FOR select_statement;
    • Oracle 或者 PostgreSQL

       DECLARE cursor_name CURSOR IS select_statement;

    select_statement:要使用 SELECT 语句来获取数据结果集,而此时还没有开始遍历数据,

    所以这里要通过SELECT 语句,返回一个用于创建游标的结果集。

  2. 打开游标

    定义好游标之后,使用游标前必须先打开游标。

    打开游标的时候 SELECT 语句的查询结果集就会送到游标工作区,为后面游标的逐条读取结果集中的记录做准备。

     OPEN cursor_name
  3. 使用游标

    使用即通过它来获取数据

     FETCH cursor_name INTO var_name [, var_name] ...
     #这句的作用是使用 cursor_name 这个游标来读取当前行,并且将数据保存到 var_name 这个变量中,
     #游标指针指到下一行。

    注意:

    • 如果游标读取的数据行有多个列名,则在 INTO 关键字后面赋值给多个变量名即可。

    • var_name必须在声明游标之前就定义好。

    • 游标的查询结果集中的字段数,必须跟 INTO 后面的变量数一致

  4. 关闭游标

     CLOSE cursor_name

    使用完游标后需要关闭掉该游标 。下次还要使用就要重新打开游标。

    游标会占用系统资源 ,如果不及时关闭,游标会一直保持到存储过程结束,影响系统运行的效率。

示例

创建存储过程“get_count_by_limit_total_salary()”,

声明IN参数 limit_total_salary,DOUBLE类型;

声明 OUT参数total_count,INT类型。

函数的功能可以实现累加薪资最高的几个员工的薪资值,直到薪资总和达到limit_total_salary参数的值,返回累加的人数给total_count。

 DELIMITER // 
 ​
 CREATE PROCEDURE get_count_by_limit_total_salary(
     IN limit_total_salary DOUBLE,OUT total_count INT)
 BEGIN
 DECLARE sum_salary DOUBLE DEFAULT 0; #记录累加的总工资 
 DECLARE cursor_salary DOUBLE DEFAULT 0; #记录某一个工资值 
 DECLARE emp_count INT DEFAULT 0; #记录循环个数 
 #定义游标 
 DECLARE emp_cursor CURSOR FOR SELECT salary 
                               FROM employees 
                               ORDER BY salary DESC;
 #打开游标 
 OPEN emp_cursor; 
 ​
         REPEAT
             #使用游标(从游标中获取数据)
             FETCH emp_cursor INTO cursor_salary; 
             SET sum_salary = sum_salary + cursor_salary; 
             SET emp_count = emp_count + 1; 
 ​
             UNTIL sum_salary >= limit_total_salary 
         END REPEAT; 
         SET total_count = emp_count; 
 #关闭游标 
 CLOSE emp_cursor; 
 ​
 END // 
 DELIMITER ;

总结

  • 优点:为逐条读取 结果集中的数据,提供了完美的解决方案。跟在应用层面实现相同的功能相比,游标可以在存储程序中使用,效率高,程序也更加简洁。

  • 缺点:比如在使用游标的过程中,会对数据行进行 加锁 ,这样在业务并发量大的时候,不仅会影响业务之间的效率,还会消耗系统资源 ,造成内存不足,这是因为游标是在内存中进行的处理。

建议:养成用完之后就关闭的习惯,这样才能提高系统的整体效率


触发器

应用场景

有 2 个或者多个相互关联的表,如商品信息和库存信息 分别存放在 2 个不同的数据表中,我们在添加一条新商品记录的时候,为了保证数据的完整性,必须同时在库存表中添加一条库存记录。

这样一来,我们就必须把这两个关联的操作步骤同步写到程序里面。可对数据进行手动维护时,很容易忘记其中的一步 ,导致数据缺失。

这时可以使用触发器:创建一个触发器,让商品信息数据的插入操作自动触发库存数据的插入操作。

定义

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

触发器是由 事件来触发 某个操作,这些事件包括 INSERT 、 UPDATE 、 DELETE 事件。

所谓事件就是指 用户的动作或者触发某项行为。

如果定义了触发程序,当数据库执行这些语句时候,就相当于事件发生了,就会自动激发触发器执行相应的操作。

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

使用

触发器不能更新或覆盖;为了修改一个触发器,必须先删再建。

使用触发器

  • insert 触发器

  1. 在insert触发器代码内,可引用一个名为new的虚拟表,访问被插入的行;

  2. 在before insert触发器内,new中的值也可以被更新(允许更改被插入的值);

  • delete 触发器

  1. 在delete触发器代码内,可以引用一个名为old的虚拟表,访问被删除的行;

  2. old中的值全都是只读的,不能更新;

  • update 触发器

  1. 在update触发器代码内,可以引用一个名为old的虚拟表访问以前(update语句前)的值,引用一个名为new的虚拟表访问新更新的值;

  2. 在before update触发器中,new中的值可能也被更新(允许更改将要用于update语句中的值);

  3. old中的值全都是只读的,不能更新;

另外的 tips :

  1. 创建触发器可能需要特殊的安全访问权限,但触发器的执行是自动的。如果insert、update或delete语句可以执行,则相应触发器也能执行;

  2. 应该用触发器来保证数据的一致性(大小写、格式等);优点在于它总是进行这种处理,而且是透明的进行,与客户机应用无关;

  3. 触发器的一种非常有意义的使用是创建审计跟踪。使用触发器,把更改记录到另一个表非常容易;

  4. MySQL触发器不支持call语句,即不能从触发器内调用存储过程。所需的存储过程代码需要复制到触发器内。

创建

 CREATE TRIGGER 触发器名称
 {BEFORE|AFTER}
 {INSERT|UPDATE|DELETE} 
 ON 表名 
 FOR EACH ROW 
 触发器执行的语句块;
  • 表名 :表示触发器监控的对象。

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

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

查看

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

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

     SHOW TRIGGERS\G
  2. 查看当前数据库中某个触发器的定义

     SHOW CREATE TRIGGER 触发器名
  3. 从系统库information_schema的TRIGGERS表中查询“salary_check_trigger”触发器的信息。

     SELECT * FROM information_schema.TRIGGERS;

删除

 DROP TRIGGER IF EXISTS 触发器名称;

触发器作为数据库对象之一,也适用于DROP删除。

示例

 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 ;

一个注意点:

如果在子表中定义了外键约束,并且外键指定了ON UPDATE/DELETE CASCADE/SET NULL子句,此

时修改父表被引用的键值或删除父表被引用的记录行时,也会引起子表的修改和删除操作,此时基于子

表的UPDATE和DELETE语句定义的触发器并不会被激活。

优缺点

  • 优点:

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

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

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

  • 缺点:

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

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

       #创建触发器用于修改会员储值操作。如果触发器中的操作出了问题,会导致会员储值金额更新失败
       update demo.membermaster set memberdeposit=20 where memberid = 2; 
       ​
       ERROR 1054 (42S22): Unknown column 'aa' in 'field list'

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

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

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

拓展:MySQL 8.0的新特性全局变量的持久化

在MySQL数据库中,全局变量可以通过SET GLOBAL语句来设置。

例如,设置服务器语句超时的限制,可以通过设置系统变量max_execution_time来实现:

 SET GLOBAL MAX_EXECUTION_TIME=2000;

但使用SET GLOBAL语句设置的变量值只会临时生效 : 即数据库重启 后,服务器又会从MySQL配置文件中读取变量的默认值。

MySQL 8.0版本新增了 SET PERSIST 命令。

例如:设置服务器的最大连接数为1000:

 SET PERSIST global max_connections = 1000;

MySQL会将该命令的配置保存到数据目录下的 mysqld-auto.cnf 文件中,下次启动时会读取该文件,用其中的配置来覆盖默认的配置文件。

相关文章:

  • Python数据分析案例11——灰色预测法预测时间序列数据
  • 刷爆leetcode第四期 0011~0015
  • 二次型和矩阵正定的意义
  • Springboot整合RabbitMQ
  • 量化投资交易接口究竟能否取缔手工交易?
  • didChangeDependencies什么时候被调用
  • 【测试面试】2022年软件测试面试题大全(精品带回答的)
  • Java --- JVM年轻代与老年代
  • 制作地图的布局、元素和设计介绍
  • C/C++面试准备——运算符
  • 【华为机考】ACM输入输出(中等+)
  • 美团四年,字节三年这七年测试之路希望能让正在迷茫的你少走弯路
  • Vue 基础
  • Ts/Typescript基础运用
  • 嵌入注意力机制的多尺度深度可分离表情识别--2021.宋玉琴
  • Centos6.8 使用rpm安装mysql5.7
  • css系列之关于字体的事
  • Docker 1.12实践:Docker Service、Stack与分布式应用捆绑包
  • JavaWeb(学习笔记二)
  • js如何打印object对象
  • ng6--错误信息小结(持续更新)
  • React Transition Group -- Transition 组件
  • scala基础语法(二)
  • 力扣(LeetCode)22
  • 猫头鹰的深夜翻译:JDK9 NotNullOrElse方法
  • 配置 PM2 实现代码自动发布
  • 如何优雅地使用 Sublime Text
  • 软件开发学习的5大技巧,你知道吗?
  • 微服务核心架构梳理
  • 一个SAP顾问在美国的这些年
  • 分布式关系型数据库服务 DRDS 支持显示的 Prepare 及逻辑库锁功能等多项能力 ...
  • 函数计算新功能-----支持C#函数
  • 交换综合实验一
  • #android不同版本废弃api,新api。
  • #define,static,const,三种常量的区别
  • #我与Java虚拟机的故事#连载19:等我技术变强了,我会去看你的 ​
  • (+4)2.2UML建模图
  • (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示
  • (二十五)admin-boot项目之集成消息队列Rabbitmq
  • (转) ns2/nam与nam实现相关的文件
  • .NET CLR Hosting 简介
  • .net core 3.0 linux,.NET Core 3.0 的新增功能
  • .NET Framework 的 bug?try-catch-when 中如果 when 语句抛出异常,程序将彻底崩溃
  • .NET LINQ 通常分 Syntax Query 和Syntax Method
  • .Net Memory Profiler的使用举例
  • .net MVC中使用angularJs刷新页面数据列表
  • .NET/C# 判断某个类是否是泛型类型或泛型接口的子类型
  • :=
  • @manytomany 保存后数据被删除_[Windows] 数据恢复软件RStudio v8.14.179675 便携特别版...
  • @select 怎么写存储过程_你知道select语句和update语句分别是怎么执行的吗?
  • @staticmethod和@classmethod的作用与区别
  • [ C++ ] STL_list 使用及其模拟实现
  • [Android]常见的数据传递方式
  • [AndroidStudio]_[初级]_[修改虚拟设备镜像文件的存放位置]
  • [BUG] Hadoop-3.3.4集群yarn管理页面子队列不显示任务