SQL 存储过程和函数的对比、变量、条件和处理程序、游标、流程控制详解+代码示例

本文涉及的产品
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS AI 助手,专业版
简介: SQL 存储过程和函数的对比、变量、条件和处理程序、游标、流程控制详解+代码示例

1. 存储过程和函数在实际项目中的使用


存储过程和函数是SQL中非常实用的功能,它们可以大量简化SQL代码和优化SQL效率,但是在实际项目中应该尽量少用存储过程和函数


理由如下:


移植性差。例如在MySQL中的存储过程移植到SQL Sever上就不一定可以用了。

调试麻烦。目前没有专门为SQL开发的用于调试的IDE,无法像VS和Eclipse一样有明确的报错提示。

扩展性不高。存储过程是一种面向过程的方法,面对复杂的业务逻辑,过程化的处理会很吃力。

所以在大型项目中应该尽量少使用甚至不使用存储过程和函数。


2. 存储过程与函数的比较


共同点


调用时,只需要提供存储过程名或者函数名,以及参数信息,而无需将SQL语句发送到服务器,节省了网络开销。

可以重复使用。减少数据库开发人员和应用程序开发人员的工作量。

可以增强数据的安全访问控制。可以设定用户的执行权限。


不同点


函数必须有且仅有一个返回值,且必须指定返回值数据类型(字符串/数值)。存储过程可以没有返回值,也可以有返回值,甚至可以有多个返回值,且所有的返回值需要使用out或者inout参数定义。

函数体内可以使用select into语句为某个变量赋值,但不能使用select语句返回结果或结果集。存储过程则没有这方面的限制,存储过程甚至可以返回多个结果集。

函数可以直接嵌入到SQL语句中,而且函数可以用于扩展标准的SQL语句。存储过程一般需要单独调用,并不会嵌入到SQL语句中使用,调用时需要使用call关键字。

函数中的函数体限制比较多,比如函数体内不能使用打开、开始或结束事务的语句,如start transaction、commit、rollback或者set autocommit=0等语句,不能在函数体内使用预处理SQL语句。存储过程的限制相对就比较少,基本上所有的SQL语句或MySQL命令都可以在存储过程中使用。

应用程序调用函数时,通常将函数添加到SQL语句中进行调用。应用程序调用存储过程时,必须使用call关键字进行调用,如果应用程序希望获取存储过程的返回值,就必须给存储过程的out参数或者inout参数传递SQL会话变量,才能通过该会话变量获取存储过程的返回值。


3. 变量的定义和使用


3.1. 变量的定义


3.1.1. 定义局部变量 declare


关键字declare用于在存储过程或者函数的begin end内部定义一个局部变量,作用范围在begin到end语句块之间。


语法如下:


declare [变量名1],[变量名2],...,[变量名n] [数据类型] [default [默认值]];


如语法所示,在declare关键字后面要指定变量名。变量名要注意尽量别使用关键字,不然容易出错。

指定变量的数据类型及其大小,变量可以用任何MySQL的数据类型,如int,varchar, datatime等。

定义出的变量初始值为Null,可以通过使用default关键字为变量指定默认值。


例如:


create procedure test(in i int)
begin
  declare username varchar(20); -- 定义了一个varchar类型的局部变量username
  declare adate, another date; -- 定义了两个data类型的局部变量username
    declare total integer default 0; -- 定义一个varchar类型的局部变量username
end


3.1.2. 定义全局变量 set @


定义全局变量可以使用set语句,语法如下:


set @[变量名] = [值];


定义全局变量时,sql会自动判断值类型,这种变量要在变量名称前面加上“@”符号,表示一个会话变量,在会话的整个生命周期结束之前它都是有作用的。


例如:


set @var = 7; -- 定义一个值为7的全局变量var
set @username = '西瓜'; -- 定义一个值为'西瓜'的全局变量username


3.2. 变量值的修改


3.2.1. 修改变量的值 set select


set关键字用于修改变量的值,例如:


create procedure test(in i int)
begin
  declare total integer default 0; -- 定义局部变量total,初始化为0;
  set @var = 5; -- 定义全局变量var,初始化为7
  set total = 1; -- 将局部变量total的值修改为1
  set @var = 25; -- 将全局变量var的值修改为3
end


select关键字也可以用来修改变量的值,例如:


create procedure test()
begin
  declare total integer default 0; -- 定义局部变量total,初始化为0;
  select 10 into total; -- 将局部变量total的值修改为10
  select total; -- 返回total
end


3.2.2. 将查询结果赋给变量 select into


在存储过程或者函数的处理过程中,通常需要获取表中查询到的值,这时可以使用select…into…语句来获取查询结果并赋值给局部变量,例如:


create procedure test()
begin
  declare total integer default 0; -- 定义局部变量total,初始化为0;
  -- 从表prot_user中找到user_name为ghj的记录,将该记录的mobile赋值给total
  select prot_user.mobile into total 
  from prot_user
  where prot_user.user_name="ghj";
  select total; -- 返回total
end


3.3. 变量值的作用域


一个变量有自己的范围(作用域),它用来定义它的生命周期。如果在存储过程中声明一个变量,那么当达到存储过程的end语句时,它将超出作用范围,因此在其它代码块中无法访问。


如果在begin…end语句块内声明一个变量,那么如果语句执行到了end,就超出了作用范围。可以在不同的作用域中声明具有相同名称的两个或多个变量,因为变量仅在自己的作用域中有效。


但是,在不同范围内声明具有相同名称的变量不是很好的编程习惯。需要注意的是,以@符号开头的变量是会话变量。直到会话结束前它都是可用的和可访问的。


4. 条件和处理程序的定义


在高级编程语言中为了提高语言的安全性,往往会提供异常处理机制。对于sql而言,也提供了一种机制来提高安全性,那就是条件。


条件的定义和处理可以用来定义在处理过程中遇到问题时相应的处理步骤,定义条件和处理程序是事先定义程序执行过程中可能遇到的问题,并且可以在处理程序中定义解决这些问题的办法,可以简单理解为异常处理,这种方法需要提前预测可能出现的问题,并提出解决办法,可以增强程序健壮性,避免程序异常停止。在mysql中定义条件和处理程序通过declare关键字。


4.1. 定义条件


条件的定义就是事先定义程序执行过程中可能遇到的问题。

语法为:


declare [条件名] condition for [错误类型]
-- 条件类型:用于定义MySql的错误
-- [错误类型] 二选一,[sqlstate 'sql状态值'] 或 [mysql错误代码] 都可以代表错误,只需填写一个即可


错误条件的类型说明中,sql状态值参数和mysql错误代码参数只需填写一个即可,用于表示mysql的错误,如常见的错误1146(42s02),sql状态值是42s02,mysql错误代码参数值是1146。


示例,如定义主键重复错误error 1062 (23000): duplicate entry '60' for key 'primary':


-- 形式一:
declare primary_key_duplicate condition for sqlstate '23000';
-- 形式二:
declare primary_key_duplicate condition for 1062;


4.1. 定义处理程序


处理程序对遇到的这些间题时应采取相应的处理方式,并且保证存储过程或函数在遇到警告或者错误的的候能继续执行。


语法:


declare [错误处理方式] handler for [错误类型] [SQL语句]


-- [错误处理方式]:有三种处理方式
-- -- -- continue:表示遇到错误不处理,继续执行
-- -- -- exit:表示遇到错误马上退出
-- -- -- undo:表示遇到错误回到前一次的操作
-- [SQL语句]可以是一个语句块,在发生定义的错误时执行的代码
-- [错误类型] 二选一,[sqlstate 'sql状态值'] 或 [mysql错误代码] 都可以代表错误,只需填写一个即可,也可以填写下面的广义错误类型
-- -- -- sqlwarning 捕获警告错误
-- -- -- not found 捕获无数据错误
-- -- -- sqlexception 捕获其他类型的错误


例如,已经定义了主键重复错误error 1062 (23000): duplicate entry '60' for key 'primary'


要对该错误继续处理,有两种方法,如下所示


-- 捕获sql状态值
-- 如果遇到sql状态值为23000的,就执行continue操作,并且设置info变量为“捕捉到错误”
declare continue handler for sqlstate '23000' set @info='捕捉到错误';
-- 捕获mysql错误代码
-- 如果遇到mysql错误代码为1062的,就执行continue操作,并且设置info变量为“捕捉到错误”
declare continue handler for 1062 set @info='捕捉到错误';
-- 或者使用广义错误状态
declare continue handler for sqlexception set @info='捕捉到错误';
-- 要是不清楚错误的种类,就全加上
declare continue handler for sqlwarning, not found, sqlexception set @info='捕捉到错误';


条件和处理程序完整的使用示例:


create procedure test()
begin
  -- 定义提示信息
  declare info varchar(10) default '没有问题';
  -- 定义主键重复错误(23000)
  -- declare primary_error condition for 1062;
  declare primary_error condition for sqlstate '23000';
  -- 定义处理程序,用于处理主键重复错误(23000)
  -- declare continue handler for 1062 set info='捕获到错误';
  declare continue handler for sqlstate '23000' set info='捕获到错误';
  -- 执行插入语句,若有23000错误抛出,则改变info值
  insert into prot_product values(1, 'test');
  -- 返回info值,查看是否发生主键重复错误
  select info;
end


5. 游标


数据库开发人员编写存储过程或者函数等存储程序时,有时需要存储程序中的MySQL代码扫描select结果集中的数据,并对结果集中的每条记录进行简单处理,通过MySQL的游标机制可以解决此类问题。


游标的使用可以概括为声明游标、打开游标、从游标中提取数据以及关闭游标。

例如下列代码:


create procedure test()
begin
  declare temp_id int; -- 定义临时id用于存储游标获取的id
  declare temp_name varchar(10) ; -- 定义临时name用于存储游标获取的name
  declare flag int default 0; -- 定义循环变量
  -- 定义1329条件,该条件在读取完数据表的最后一条记录后被触发,只能在游标之前定义条件
  declare err condition for 1329;
  -- 声明游标
  declare cur cursor for select emp.id, emp.name from emp;
  -- 定义处理程序处理1329条件,只能在游标之后
  declare continue handler for 1329 set flag = 1;
  -- 打开游标
  open cur;
  -- 循环读取数据
  while flag = 0 do
  -- 读取游标数据
  fetch cur into temp_id, temp_name;
  select temp_id, temp_name;
  end while;
  -- 关闭游标
  close cur;
end


声明游标


declare [游标名] cursor for [select语句]


使用declare语句声明游标后,此时与该游标对应的select语句并没有执行,MySQL 服务器内存中并不存在与select语句对应的结果集。


打开游标


open [游标名]


使用open语句打开游标后,与该游标对应的select语句将被执行,MySQL 服务器内存中将存放与select语句对应的结果集。


从游标中提取数据


fetch [游标名] into [变量名1],[变量名2],...,[变量n]


变量名的个数必须与声明游标时使用的select语句结果集中P的字段个数保持一致。


第一次执行fetch语句时, fetch语句从结果集中提取第一条记录,再次执行fetch语句时,fetch语句从结果集中提取第二条记录,…以此类推。fetch语句每次从结果集中仅仅提取一条记录,因此fetch语句需要循环语句的配合,才能实现整个数据表的遍历。


当使用fetch语句从游标中提取最后一条记录后,再次执行fetch语句时,将产生“ERROR 1329(02000): No data to FETCH"错误信息,数据库开发人员可以针对MySQL错误代码1329,自定义错误处理程序以便结束遍历。需要注意的是:游标错误处理程序应该放在声明游标语句之后。


关闭游标


close [游标名]


关闭游标的目的在于释放游标打开时产生的结果集,节省MySQL服务器的内存空间。游标如果没有被明确地关闭,游标将在它被声明的begin…end语句块的末尾被强制关闭。


6. 流程控制


6.1. 条件控制语句


6.1.1. if…else 语句


if语句根据条件表达式的值确定执行不同的语句块

语法:


if [条件表达式1] then 
  [语句块1];
else if [条件表达式2] then 
  [语句块2];
else 
  [语句块n];
end if;


例如下面的if…else语句


create procedure test()
begin
  -- 定义三个临时变量
  set @number_1 = 1; 
  set @number_2 = 2;
  set @number_3 = 3;
  if @number_1 > @number3 then 
  select @number_1;
  elseif @number_2 > @number3 then 
  select @number_2;
  else
  select @number_3;
  end if;
end
-- 返回3


6.1.2. case…when 语句


case语句用于实现比if语句分支更为复杂的条件判断

语法:


case [表达式]
  when [值1] then 
  [语句块1];
  when [值2] then 
  [语句块2];
  else 
  [语句块n];
end case;


需要注意的是MySQL中的case语句与C语言、Java语言等高级程序设计语言不同,在高级程序设计语言中,每个case的分支需使用"break"跳出,而MySQL无需使用"break"语句。


例如下面的case语句:


create procedure test()
begin
  declare sex char(2) default '0'; 
  case sex
  when '0' then
    select '女' as 'sex';
  when '1' then
    select '男' as 'sex';
  else
    select 'Null' as 'sex';
  end case;
end


6.2. 循环语句


6.2.1. 循环控制语句leave与iterate


循环标签可以帮助循环体控制循环的运行,提高循环的灵活性。leave语句用于结束循环;iterate语句用于跳出本次循环,继而进行下次循环。


语法:


-- leave标签
leave [循环标签];
-- iterate标签
iterate [循环标签];


6.2.2. while 语句


当条件表达式的值为true时,反复执行循环体,直到条件表达式的值为false。


语法:


[循环标签]: while [条件表达式] do
  [循环体];
end while [循环标签];


例如:


create procedure test()
begin
  declare count int default 0; 
  -- 当count小于10时进行循环
  xh: while count<10 do
  set count = count + 1; -- count自加1
  -- 当count大于5时退出循环
  if count > 5 then
    leave xh; -- 结束标签为xh的循环
  end if;
  end while xh;
  select count;
end


6.2.3. repeat 语句


与while相反,当条件表达式的值为false时,反复执行循环,直到条件表达式的值为true。

语法:


[循环标签]: repeat
  [循环体];
until [条件表达式]
end repeat [循环标签];


例如:


create procedure test()
begin
  declare count int default 0; 
  -- 执行循环
  xh:repeat
  set count = count + 1; -- count自加1
  until count>=10 -- 直到count大于等于10,则结束循环
  end repeat xh;
  select count;
end
-- 返回值为10


6.2.4. loop 语句


由于loop循环语句本身没有停止循环的语句,因此loop通常使用leave语句跳出loop循环。

语法:


[循环标签]: loop
  [循环体];
  if [条件表达式] then
  leave [循环标签];
  end if;
end loop [循环标签];


例如:


create procedure test()
begin
  declare count int default 0; 
  xh: loop
  set count = count + 1;
  if count >= 10 then
    leave xh;
  end if;
  end loop xh;
  select count;
end
-- 返回 10
相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
4月前
|
SQL 数据可视化 关系型数据库
MCP与PolarDB集成技术分析:降低SQL门槛与简化数据可视化流程的机制解析
阿里云PolarDB与MCP协议融合,打造“自然语言即分析”的新范式。通过云原生数据库与标准化AI接口协同,实现零代码、分钟级从数据到可视化洞察,打破技术壁垒,提升分析效率99%,推动企业数据能力普惠化。
406 3
|
10月前
|
SQL 自然语言处理 数据库
【Azure Developer】分享两段Python代码处理表格(CSV格式)数据 : 根据每列的内容生成SQL语句
本文介绍了使用Python Pandas处理数据收集任务中格式不统一的问题。针对两种情况:服务名对应多人拥有状态(1/0表示),以及服务名与人名重复列的情况,分别采用双层for循环和字典数据结构实现数据转换,最终生成Name对应的Services列表(逗号分隔)。此方法高效解决大量数据的人工处理难题,减少错误并提升效率。文中附带代码示例及执行结果截图,便于理解和实践。
282 4
|
7月前
|
SQL 缓存 监控
SQL 质量革命:利用 DAS 智能索引推荐修复慢查询全流程
在数据驱动时代,数据库性能直接影响系统稳定与响应速度。慢查询常因索引缺失、复杂逻辑或数据量过大引发,导致延迟、用户体验下降甚至业务受损。DAS(数据库管理服务)提供智能索引推荐功能,通过分析SQL语句与数据分布,自动生成高效索引方案,显著提升查询性能。本文结合实战案例,详解DAS智能索引推荐原理与使用流程,帮助用户快速定位问题并优化数据库表现,实现系统高效运行。
418 61
|
10月前
|
SQL
【YashanDB知识库】like 变量的SQL语句应用程序执行效率低与yasql执行效率高
【YashanDB知识库】like 变量的SQL语句应用程序执行效率低与yasql执行效率高
|
SQL Java 数据库连接
如何在 Java 代码中使用 JSqlParser 解析复杂的 SQL 语句?
大家好,我是 V 哥。JSqlParser 是一个用于解析 SQL 语句的 Java 库,可将 SQL 解析为 Java 对象树,支持多种 SQL 类型(如 `SELECT`、`INSERT` 等)。它适用于 SQL 分析、修改、生成和验证等场景。通过 Maven 或 Gradle 安装后,可以方便地在 Java 代码中使用。
4037 11
|
SQL 关系型数据库 MySQL
创建SQL数据库的基本步骤与代码指南
在信息时代,数据管理显得尤为重要,其中数据库系统已成为信息技术架构的关键部分。而当我们谈论数据库系统时,SQL(结构化查询语言)无疑是其中最核心的工具之一。本文将详细介绍如何使用SQL创建数据库,包括编写相应的代码和必要的步骤。由于篇幅限制,本文可能无法达到您要求的2000字长度,但会尽量涵盖创建数
462 3
|
SQL 缓存 关系型数据库
揭秘MySQL一条SQL语句的执行流程
以上步骤共同构成了MySQL处理SQL语句的完整流程,理解这一流程有助于更有效地使用MySQL数据库,优化查询性能,及时解决可能出现的性能瓶颈问题。
339 8
|
SQL 监控 关系型数据库
SQL错误代码1303解析与处理方法
在SQL编程和数据库管理中,遇到错误代码是常有的事,其中错误代码1303在不同数据库系统中可能代表不同的含义
|
SQL 安全 关系型数据库
SQL错误代码1303解析与解决方案:深入理解并应对权限问题
在数据库管理和开发过程中,遇到错误代码是常见的事情,每个错误代码都代表着一种特定的问题