【SQL】存储过程、函数、触发器

news/2025/2/1 14:04:28/文章来源:https://www.cnblogs.com/upstudy/p/18692974

存储过程

存储过程(Stored Procedure)是一种在数据库中保存的SQL语句集合,它可以执行一系列的数据库操作,例如插入、更新、查询等。存储过程可以提高数据库操作的效率,减少网络流量,并且可以封装复杂的逻辑

  • 定义:
    存储过程一组为了完成特定功能的SQL语句集,这些语句被存储在数据库中,可以重复使用
  • 性能:
    因为存储过程在数据库服务器上执行,减少了网络传输的数据量,提高了执行效率。
  • 安全性:
    通过限制对数据库的直接访问,增强了数据的安全性。
  • 重用性:
    相同的存储过程可以被多个应用程序调用,减少代码重复。

语法结构

CREATE DEFINER=`root`@`localhost` PROCEDURE Demo(IN START INT(10),IN max_num INT(10))		
# DEFINER=`root`@`localhost` 指定所有者,标识【root用户】,【本地主机】才可以执行该存储过程,否则无法执行(可以不写)
# Demo为定义的存储过程名,()内为参数,可以没有参数
# 参数可以是输入(IN)、输出(OUT)或输入/输出(INOUT)类型的
BEGINSELECT 'Hello, World!';		# 过程体语句
END


调用

call Demo(5,@Count);  # 调用Demo存储过程,并传入参数5,并定义输出参数(根据参数位置进行定义,参数命名需要@开头名字任意字符进行定义)
select @Count;   # 打印输出参数

执行结果

示例:批量插入多条数据

CREATE DEFINER=`root`@`localhost` PROCEDURE `insert_course`(IN START INT(10),IN max_num INT(10))	
BEGIN
DECLARE i INT DEFAULT 0;
SET autocommit = 0;
REPEATSET i = i + 1;INSERT INTO course (teacher_id,name) VALUES (i+1,"计算机");
UNTIL i = max_num
END REPEAT;
COMMIT;
END

调用

call insert_course(1,10);

执行结果

存储过程的应用场景

  • 数据库事务处理
    存储过程可以将多个SQL语句组合在一起,以实现事务处理。如果其中任何一个SQL语句失败,整个事务将被回滚,从而确保数据的一致性和完整性。
  • 简化数据库操作
    存储过程可以将复杂的SQL语句封装起来,客户端只需调用存储过程名即可执行复杂的操作,简化了客户端与数据库之间的交互。
  • 提高性能
    存储过程在数据库中编译后,可以被多个客户端共享,减少了网络传输的开销,提高了执行效率。此外,存储过程可以利用数据库的优化器和索引等特性,进一步提高查询性能。
  • 保证数据一致性
    存储过程可以封装业务逻辑和数据验证逻辑,确保只有符合规则的数据才能被插入或更新,从而保证了数据的一致性和安全性。
  • 自动化和批处理
    存储过程可以封装批量插入、更新和删除等操作,实现数据的自动化处理。此外,存储过程还可以与触发器、事件等结合使用,实现更高级别的自动化和批处理功能。
  • 业务逻辑处理
    当业务逻辑复杂时,可以将这些逻辑封装在存储过程中,提高代码的可读性和可维护性。(如:数据清洗、数据转移、批处理任务等。)
  • 安全控制
    通过存储过程,可以对输入参数进行验证,提高数据的安全性。

函数

函数与存储过程类似,但与存储过程不同的是,函数必须返回一个值。函数通常用于计算返回数据
存储函数是有返回值的存储过程,存储函数的参数只能是in类型的。

存储函数必须要有返回值

CREATE FUNCTION function_name (parameter_list)
RETURNS return_datatype
[characteristic ...]
BEGIN-- 函数体RETURN value;
END;参数说明:
function_name: 函数的名称。
parameter_list: 函数的参数列表,可以有多个参数,每个参数由参数名和数据类型组成。
return_datatype: 函数返回的数据类型。
characteristic: 函数的特性,例如 DETERMINISTIC(表示函数对于给定的输入总是返回相同的结果)。
BEGIN ... END;: 函数体,其中包含了函数的 SQL 语句和逻辑。
RETURN value;: 函数返回一个值。

示例:定义一个返回字符串长度的函数

DELIMITER $$CREATE FUNCTION get_string_length(input_string VARCHAR(255))
RETURNS INT
DETERMINISTIC
BEGINRETURN CHAR_LENGTH(input_string);
END$$DELIMITER ;

示例:定义一个计算两个数字和的函数

DELIMITER $$CREATE FUNCTION add_numbers(num1 INT, num2 INT)
RETURNS INT
DETERMINISTIC
BEGINRETURN num1 + num2;
END$$DELIMITER ;

调用函数

select add_numbers(1,3);

函数删除操作

drop function add_numbers;

函数的应用场景

  • 数据清洗和格式化
    • 日期格式化
      使用DATE_FORMAT函数可以将系统时间转化成指定格式的字符串。例如,查询2019年12月份的数据可以使用DATE_FORMAT(clock_time,'%Y-%m')='2019-12'
    • 字符串拼接
      使用CONCAT函数可以将多个字符串连接在一起。例如,CONCAT('Hello', ' ', 'MySQL')可以生成Hello MySQL
    • 字符串截取
      使用SUBSTRING函数可以截取字符串的一部分。例如,SUBSTRING('Hello MySQL', 1, 5)会返回Hello
  • 数据分析
    • 日期计算
      使用DATEDIFF函数计算两个日期之间的天数差,例如计算入职天数。使用DATEDIFF(current_date, hire_date)可以计算当前日期与入职日期之间的天数。
    • 聚合函数
      使用COUNT()、SUM()、AVG()、MAX()和MIN()等聚合函数进行数据汇总和统计分析。例如,计算表中的行数可以使用COUNT(*),计算某列的总和可以使用SUM(column)
  • 数据处理和转换
    • 条件判断
      使用IF()CASE WHEN...THEN...ELSE...END进行条件判断和数据转换。例如,根据条件返回不同的值可以使用IF(condition, value_if_true, value_if_false)
    • 数值处理
      使用ABS()、ROUND()、RAND()等函数进行数值处理。例如,四舍五入数字到指定的小数位数可以使用ROUND(num, dec)
  • 特定业务场景的应用
    • 员工入职天数计算
      在企业的OA系统中,可以使用MySQL函数计算员工入职天数。例如,使用DATEDIFF(current_date, hire_date)计算当前日期与入职日期之间的天数差。
    • 分数等级判定
      在做报表时,可以使用MySQL函数快速判定分数的等级。例如,使用条件判断函数根据分数值判定等级。

触发器

触发器(Trigger)是一种特殊的存储过程。
它与表有关,当表上的特定事件insert,update,delete)发生时,触发器会自动执行
可以使用触发器来实现数据约束,数据验证,数据复制等功能。

create trigger 触发器名称
{before | after } {insert | update | delete}  -- 触发器类型和事件
on 表名称
for each row  -- 触发器的作用范围
begin-- 触发器执行的操作
end;参数说明:
before / after : 表示触发器的类型,分别表示发生前/发生后执行
insert / update / delete : 表示触发器的事件类型,分别表示插入 / 更新 / 删除操作
on 表名称 : 为触发器所在的表名
for each row : 表示触发器作用的范围,即每一行记录都会触发该触发器
begin 和 end之间是触发器执行的操作,可以是一条或者多条SQL语句

调用方式

触发器是自动执行的,无需手动调用
当表上的特定事件insert , update, delete) 发生时,触发器会自动执行
在创建触发器时,可以定义触发器的类型和事件,从而控制触发器的时机和条件。

示例:添加事件

-- 创建一个触发器,当向表中插入一条记录时,自动向另一个表中插入一条记录
create trigger insert_trigger_1
after insert on table1for each rowbegin-- 触发器的具体事件insert into table2(id,name) values (NEW.id,New.name);end;

添加数据

insert into table1(id,name) values (1,'kobe'),(2,'lebron'),(3,'curry'),(4,'durant'),(5,'paul'),(6,'westbrook');
向table1表中添加数据时,会触发insert_trigger_1触发器,自动向table2表中添加数据

同理删除事件/修改事件,则将触发器事件类型insert改为delete/update,sql语句改为删除语句

删除触发器

drop trigger 触发器名称;

触发器的应用场景

  • 强制实施业务规则
    • 通过在触发器中编写逻辑,可以在特定的表上自动执行业务规则,例如检查输入的数据是否符合要求,或者限制某些操作的执行。
  • 记录日志变更
    • 通过在触发器中编写逻辑,可以在特定的表上自动记录数据的变更情况,例如记录数据的修改时间、修改人等信息。
  • 复杂的默认值计算
    • 通过在触发器中编写逻辑,可以在特定的表上自动计算默认值,例如根据其他字段的值计算出一个新的字段的值。
  • 数据同步
    • 通过在触发器中编写逻辑,可以在多个表之间自动同步数据,例如在一个表中插入一条数据时,自动在另一个表中插入相应的数据。
  • 数据校验
    • 通过在触发器中编写逻辑,可以在特定的表上自动校验数据的正确性,例如检查数据的唯一性、完整性等。

存储过程/函数/触发器区别

定义和用途

  • 触发器(Trigger)
    触发器是一种特殊的存储过程,它不是由程序调用,而是通过事件(如INSERT、UPDATE、DELETE操作)自动触发执行。触发器主要用于数据验证、审计、日志记录和自动更新等场景,确保数据的一致性和完整性。
  • 存储过程(Stored Procedure)
    存储过程是一组为了完成特定功能的SQL语句和控制结构的集合,存储在数据库中。它们可以被外部程序调用,用于执行复杂的数据库操作,如数据转换、数据迁移和报表生成。存储过程可以接受参数、返回结果,并且可以隐藏数据库的复杂性,简化应用开发。
  • 存储函数(Stored Function)
    存储函数与存储过程类似,但必须返回一个值。它们通常用于计算字段值或执行需要返回结果的数据库操作。

存储过程/函数/触发器 语法

# 查看存储过程的定义
show create procedure 存储过程名;
# 查询指定数据库的存储过程及状态信息
select * from information_schema.routines where routine_schema='数据库名';
# 删除存储过程
drop procedure 存储过程名;

变量

系统变量

系统变量是mysql服务器提供的,分为全局变量(global)、会话变量(session)

show global variables;  # 查看所有变量(不写global是检索当前会话的变量,写global是检索mysql全局的变量)show global variables like '匹配内容';  # 通过like模糊匹配查找变量,_匹配单字符,%匹配多字符

用户变量

用户可以自定义变量,不需提前声明。
用户变量的使用只限于当前会话(当前查询会话)。

# 赋值语句
set @变量名=值;
select 字段名 into @变量名 from 表名; # 直接把表内字段的数据作为变量的值,方式一
select 字段名  from 表名 into @变量名;# 直接把表内字段的数据作为变量的值,方式二# 查看变量的值
select @变量名; 

示例

# 定义用户变量
set @aaa="123",@ccc="333";	
select count(*) INTO @bbb from score where student_id = 4 ;
# 查看用户变量
select @aaa,@bbb,@ccc;

局部变量

局部变量需要declare声明,其范围是在一个存储过程/函数/触发器begin到end之间

declare 变量名 变量类型[default 默认值];  # 在声明的时候可以直接设置一个默认值(变量类型有int、char、varchar等等)

示例:定义变量

DELIMITER //
CREATE DEFINER=`root`@`localhost` PROCEDURE `Demo1`(IN id INT(10),OUT subject_count INT)
BEGINdeclare s1 int default 0;	# stu局部变量默认值为0set @s2=1;				# 会话变量stsselect count(*) into s1 from score where student_id = 3 ;select @s2,s1;
END //
DELIMITER ;
call Demo1(1,@ss);
select @s2;

执行结果

declare应用场景:可以定义变量、条件处理器、游标。

delimiter 分隔符

DELIMITER命令是为了改变MySQL命令结束的标识符。在这个例子中,我们将命令的结束符设置为//以便在存储过程中处理多条语句。

begin...end

主要用于定义一个代码块,这个代码块可以用于存储过程函数触发器中。
BEGIN...END在存储过程中起到了定义代码块的作用,帮助组织管理存储过程中的逻辑

存储过程中可以有多个begin...end

if...else

if判断是在begin与end间使用

语法:if 条件1 then代码1elseif 条件2 then		#可选代码2else				#可选代码3end if;# 如果 符合条件1 则进入代码1,否则如果符合条件2则进入代码2,否则进入代码3
# 示例:
create procedure p(in sc char(10),in sc2 char(10))
begin	declare result varchar(10);declare sum int;select grade into sum from score where sno = sc and cno = sc2; if sum >= 85 thenset result = '优秀';elseif sum>=60 thenset result = '及格';elseset result = '不及格';end if;select result;
end;-- call p('2015001','c02');

case语句

方式一:值(1/2)

case 值when 值1 then 语句1when 值2 then 语句2...else 语句3
end else;# case后的值是值1就执行语句1,是值2就执行语句2

方式二:表达式

casewhen 条件表达式1 then 语句1when 条件表达式2 then 语句2...else 语句3
end else;# 条件表达式1为true就执行语句1,条件表达式2为true就执行语句2,否则执行语句3示例:
create PROCEDURE p2(in month int)
begin
declare result varchar(10);
casewhen month>=1 and month<=3 thenset result = '第一季度';when month>=4 and month<=6 thenset result = '第二季度';when month>=7 and month<=9 thenset result = '第三季度';when month>=10 and month<=12 thenset result = '第四季度';elseset result = '非法参数';end case;select concat('您输入的月份为:',month,'所属的季度为:',result);
end;
-- call p2(1);

while循环语句

# 先判断条件,如果条件为true,则执行逻辑,否则,不执行
while 条件 dosql逻辑代码
end while;# 示例
create procedure p7(in n int)
begindeclare total int default 0;while n>0 doset total :=total + n;set n :=n - 1;end while;select total;
end;# 计算从1累加到n的值,n为入参值
# 通过局部变量total,进行循环累加,直到n等于0时,则退出循环

repeat循环语句

与while语句的区别是:while是符合条件才进行循环,repeat是满足条件才跳出循环

# 先判断条件,当满足条件时退出循环
# 先执行一次逻辑,然后判定逻辑是否满足,如果满足则退出,否则继续
repeatsql逻辑代码until 条件
end repeat;# 示例
create procedure p7(in n int)
begindeclare total int default 0;repeatset total :=total + n;set n :=n - 1;until n<=0end repeat;select total;
end;

loop循环语句

loop可实现简单的循环,如果不在SQL逻辑中增加退出循环的条件,可以实现简单的死循环。
loop循环本身是没有退出条件,需要自行添加退出语句:

  • leave:配合循环使用,退出循环。
  • iterate:必须用在循环中,作用是跳过当前循环剩下的语句,直接进入下一次循环。
语法:标记名:loopSQL逻辑代码end loop 标记名;leave lable;   #退出指定标记的循环体iterate label;  #直接进入下一次循环# 示例
create procedure p7(in n int)
begindeclare total int default 0;sum:loopif n<=0 thenleave sum;end if;set total :=total + n;set n :=n - 1;end loop sum;select total;
end;

游标cursor

游标是用来存储查询结果集的数据类型,在存储过程函数中可以使用游标对结果集进行循环的处理
游标的使用包括游标游标(CURSOR)的声明、OPENFETCHCLOSE,其语法如下:

DECLARE 游标名称 CURSOR FOR 查询语句;		# 声明游标
OPEN 游标名称;		# 打开游标
FETCH 游标名称 INTO 变量,变量...;		# 获取游标记录
CLOSE 游标名称;		# 关闭游标# 示例
# 根据传入的参数age,查询students表中,所有学生出生日期小于age的用户姓名和专业,并将用户的姓名和专业插入到一个新表中
# 思路:A:声明游标,B:创建表,C:开启游标,D:获取游标中的记录,E:插入数据到新表,F:关闭游标
create PROCEDURE p7(
in uage date			#传入出生日期
)
begindeclare usname varchar(100);		#保存符合条件的学生信息的两个局部变量declare upro varchar(100);declare u_cursor cursor for 		#声明游标,select sname,smajor from students where sbirthday<uage;	#找出 出生日期大于uage的#如果存在则删除,这样的话就保证表中只显示比uage大的学生信息drop table if exists tb_user;																								create table if not exists tb_user(		#创建表id int primary key auto_increment,	#id设置自增主键uname varchar(100),sdapt varchar(100));open u_cursor;				#打开游标while true do				#进入while循环fetch u_cursor into usname,upro; 	# 把游标中的记录存入两个局部变量insert into tb_user values(null,usname,upro);	#插入新表end while;close u_cursor;		#关闭游标
end;
call p7('1999-03-1');

条件处理程序handler

handler可以用来定义在流程执行中遇到问题时的处理步骤。分为继续、停止2种。其语法如下:

declare continue handler for 状态1,状态2... SQL语句;		# 当出现语句中的状态时,继续执行当前程序
declare exit handler for 状态1,状态2... SQL语句;		# 当出现语句中的状态时,停止执行当前程序

语句中的状态有4种:

  1. sqlstate: 状态码
    状态码比如02000,代表没得到数据
  2. sqlwaring:所有以01开头的状态码
  3. not found:所有以02开头的状态码
  4. sqlexception:01、02开头以外的其他状态码

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Tools] GitHub Action 部署文档网站

关于部署网站,理论上来讲,只要你有一个服务器,你要采用什么样的方式来部署都是可以的。但是前提是你需要有一个服务器(物理机、云服务器)。 这节课我们部署文档网站选择使用 github 来进行部署,因为 GitHub 为我们提供了一个免费的服务器,一个账号只有一个,只要你在 Gi…

[Tools] VitePress搭建文档网站

创建 API 文档可以选择如下的 3 种方式:功能较少,可以直接写在 README.md 文件里面 内容较多,可以单独写一个文件 API 数量众多(Vue、React 这种级别),需要考虑单独拿一个网站来做详细的文档支持这里我们要搭建的网站实际上就是一个文档网站,这个时候我们可以选择静态站…

[Tools] 搭建文档网站

创建 API 文档可以选择如下的 3 种方式:功能较少,可以直接写在 README.md 文件里面 内容较多,可以单独写一个文件 API 数量众多(Vue、React 这种级别),需要考虑单独拿一个网站来做详细的文档支持这里我们要搭建的网站实际上就是一个文档网站,这个时候我们可以选择静态站…

java中的HsahMap

HsahMap HashMap 是 Java 中最常用的集合类之一,它实现了 Map 接口,基于哈希表存储键值对 HashMap的存储是无顺序的 HashMap存储的是键值对(key-value)其中键key是唯一的,值(value)可以重复。 HashMap的底层是数组和链表 HashMap的常见方法 添加方法: 1,put(K key, V valu…

MyBatis的增删改查实现

首先还是使用上一篇文章相同的数据表和实体类,如下图所示1 package com.loubin.pojo;2 3 public class User {4 private int id;5 private String name;6 private String pwd;7 8 public int getId() {9 return id; 10 } 11 12 public void …

Reqable:现代化 API 调试工具

Reqable 是一款专为开发者设计的现代化 API 调试工具,旨在简化 API 开发、测试和调试的流程。 它支持多种协议(如 HTTP、HTTPS、WebSocket 等),并提供了丰富的功能,帮助开发者更高效地构建、调试和分析 API 请求与响应。 Reqable 以其简洁的设计、强大的功能和卓越的性能,…

DeepSeek-R1?别信新闻,它真能打败 OpenAI?

Yy DeepSeek-R1?别信新闻 它真的是那个打败 OpenAI 的开源黑马模型,还是又一条假新闻? Fabio Matricardi它真的那么厉害……是在哪方面? 前言:DeepSeek在大模型小型以及优化的道路上探索蒸馏和纯强化学习的路径获得的一点成绩确实让美国人紧张了一把,但同时也招致了更严格…

6.演讲比赛流程管理系统

基于STL的演讲比赛流程管理系统。1.需求2.文件目录3.代码 3.1Speaker.h #pragma once #include<iostream> using namespace std;class Speaker {public:string m_Name;double m_Score[2];};3.2SpeechManager.h #pragma once #include<iostream> #include<vector…

Qwen2.5-VL:更强大的多模态大模型|附实测结果

模型更新简述 几天前,通义千问更新了最新的多模态大模型Qwen2.5-VL,拥有包含 3B、7B 和 72B 在内的 3 个模型尺寸,同时完全开源,可在huggingface和modelscope下载到所有模型权重。 1. 更灵活的时空维度处理 ● 空间维度上,动态地将不同尺寸的图像转换为不同长度的token,并…

第五节上,图像分类实战,食物分类

随机种子固定随机结果,方便复现 def seed_everything(seed):torch.manual_seed(seed)torch.cuda.manual_seed(seed)torch.cuda.manual_seed_all(seed)torch.backends.cudnn.benchmark = Falsetorch.backends.cudnn.deterministic = Truerandom.seed(seed)np.random.seed(seed)o…