24. 使用MySQL之使用游标

news/2024/11/13 13:21:08/文章来源:https://www.cnblogs.com/hisun9/p/18538839

1. 游标

由前几章可知,MySQL检索操作返回一组称为结果集的行。这组返回的行都是与SQL语句相匹配的行(零行或多行)。

使用简单的SELECT语句,例如,没有办法得到第一行、下一行或前10行,也不存在每次一行地处理所有行的简单方法(相对于成批地处理它们)。

有时,需要在检索出来的行中前进或后退一行或多行。这就是使用游标的原因。

游标(cursor)是一个存储在MySQL服务器上的数据库查询,它不是一条SELECT语句,而是被该语句检索出来的结果集。在存储了游标之后,应用程序可以根据需要滚动或浏览其中的数据。游标主要用于交互式应用,其中用户需要滚动屏幕上的数据,并对数据进行浏览或做出更改。

注意:

  • 只能用于存储过程:

    不像多数DBMS,MySQL游标只能用于存储过程(和函数)。

2. 使用游标

使用游标涉及几个明确的步骤。

  • 在能够使用游标前,必须声明(定义)它。这个过程实际上没有检索数据,它只是定义要使用的SELECT语句。

  • 一旦声明后,必须打开游标以供使用。这个过程用前面定义的SELECT语句把数据实际检索出来。

  • 对于填有数据的游标,根据需要取出(检索)各行。

  • 在结束游标使用时,必须关闭游标。

在声明游标后,可根据需要频繁地打开和关闭游标。在游标打开后,可根据需要频繁地执行取操作。

2.1 创建游标

游标用DECLARE语句创建(参见第23章)。

DECLARE命名游标,并定义相应的SELECT语句,根据需要带WHERE和其他子句。

例如,下面的语句定义了名为ordernumbers的游标,使用了可以检索所有订单的SELECT语句。

delimiter //
create procedure processorders()
begindeclare ordernumbers cursor forselect order_num from orders;
end //
delimiter ;

这个存储过程并没有做很多事情,DECLARE语句用来定义和命名游标,这里为ordernumbers。存储过程处理完成后,游标就消失(因为它局限于存储过程)

在定义游标之后,可以打开它。

2.2 打开和关闭游标

游标用OPEN CURSOR语句来打开:

open ordernumbers;

在处理OPEN语句时执行查询,存储检索出的数据以供浏览和滚动

游标处理完成后,应当使用如下语句关闭游标:

close ordernumbers;

CLOSE释放游标使用的所有内部内存和资源,因此在每个游标不再需要时都应该关闭。

在一个游标关闭后,如果没有重新打开,则不能使用它。但是,使用声明过的游标不需要再次声明,用OPEN语句打开它就可以了。

补充:

  • 隐含关闭:

    如果你不明确关闭游标,MySQL将会在到达END语句时自动关闭它。

下面是前面例子的修改版本:

delimiter //
create procedure processorders()
begin-- Declare the cursordeclare ordernumbers cursor forselect order_num from orders;-- Open the cursoropen ordernumbers;-- close the cursorclose ordernumbers;
end //
delimiter ;

这个存储过程声明、打开和关闭一个游标。但对检索出的数据什么也没做。

2.3 使用游标数据

在一个游标被打开后,可以使用FETCH语句分别访问它的每一行。

FETCH指定检索什么数据(所需的列),检索出来的数据存储在什么地方。它还向前移动游标中的内部行指针,使下一条FETCH语句检索下一行(不重复读取同一行)。

举例说明

  • 第一个例子从游标中检索单个行(第一行):

    delimiter //
    create procedure processorders()
    begin-- Declare local variablesdeclare o int;-- Declare the cursordeclare ordernumbers cursor forselect order_num from orders;-- Open the cursoropen ordernumbers;-- Get order numberfetch ordernumbers into o;-- close the cursorclose ordernumbers;
    end //
    delimiter ;
    

    其中FETCH用来检索当前行的order_num列(将自动从第一行开始)到一个名为o的局部声明的变量中。对检索出的数据不做任何处理。

  • 在下一个例子中,循环检索数据,从第一行到最后一行:

    delimiter //
    create procedure processorders()
    begin-- Declare local variablesdeclare done boolean default 0;declare o int;-- Declare the cursordeclare ordernumbers cursor forselect order_num from orders;-- Declare continue handlerdeclare continue handler for sqlstate '02000' set done = 1;-- Open the cursoropen ordernumbers;-- loop through all rowsrepeat-- Get order numberfetch ordernumbers into o;-- End of loopuntil done end repeat;-- close the cursorclose ordernumbers;
    end //
    delimiter ;
    

    与前一个例子一样,这个例子使用FETCH检索当前order_num到声明的名为o的变量中。

    但与前一个例子不一样的是,这个例子中的FETCH是在REPEAT内,因此它反复执行直到done为真(由UNTIL done END REPEAT;规定)。为使它起作用,用一个DEFAULT 0(假,不结束)定义变量done。那么,done怎样才能在结束时被设置为真呢?答案是用以下语句:

    declare continue handler for sqlstate '02000' set done = 1;
    

    这条语句定义了一个CONTINUE HANDLER,它是在条件出现时被执行的代码。这里,它指出当SQLSTATE '02000'出现时,SET done=1SQLSTATE '02000'是一个未找到条件,当REPEAT由于没有更多的行供循环而不能继续时,出现这个条件。

    插句题外话

    DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;是一种在 SQL 中定义异常处理器的语句,通常在存储过程或存储函数中使用。以下是其具体含义:

    • DECLARE CONTINUE HANDLER:声明一个处理器,当指定的错误或状态码发生时继续执行,而不会退出或中断当前的 SQL 语句块。

    • FOR SQLSTATE '02000':表示处理 SQL 状态码 '02000'。这个状态码 '02000' 通常指“没有更多的行”或“未找到数据”,例如在游标操作中遍历结束时会出现这个状态码。

    • SET done = 1:当状态码 '02000' 发生时,将变量 done 的值设置为 1。这个 done 变量通常用来判断是否已经到达数据的末尾,以控制循环结构(例如 LOOP、WHILE 等)的结束条件。

    回归正题

    如果调用这个存储过程,它将定义几个变量和一个CONTINUE HANDLER,定义并打开一个游标,重复读取所有行,然后关闭游标。

    如果一切正常,你可以在循环内放入任意需要的处理(在FETCH语句之后,循环结束之前)。

    补充:

    • MySQL的错误代码:

      关于MySQL 5使用的MySQL错误代码列表,请参阅此链接。

    • DECLARE语句的次序:

      DECLARE语句的发布存在特定的次序。用DECLARE语句定义的局部变量必须在定义任意游标或句柄之前定义,而句柄必须在游标之后定义。不遵守此顺序将产生错误消息。

    • 重复或循环?:

      除这里使用的REPEAT语句外,MySQL还支持循环语句,它可用来重复执行代码,直到使用LEAVE语句手动退出为止。通常REPEAT语句的语法使它更适合于对游标进行循环。

  • 为了把这些内容组织起来,下面给出游标存储过程样例的更进一步修改的版本,这次对取出的数据进行某种实际的处理:

delimiter //
create procedure processorders()
begin-- Declare local variablesdeclare done boolean default 0;declare o int;declare t decimal(8,2);-- Declare the cursordeclare ordernumbers cursor forselect order_num from orders;-- Declare continue handlerdeclare continue handler for sqlstate '02000' set done = 1;-- Create a table to store the resultscreate table if not exists ordertotals(order_num int, total decimal(8,2));-- Open the cursoropen ordernumbers;-- loop through all rowsrepeat-- Get order numberfetch ordernumbers into o;-- Get the total for this ordercall ordertotal(o, 1, t);-- Insert order and total into ordertotalsinsert into ordertotals(order_num, total)values(o, t);-- End of loopuntil done end repeat;-- close the cursorclose ordernumbers;
end //
delimiter ;

插一句题外话

注意在创建了这个存储过程后,一定要call(调用)它,才会生成表

即要运行这个

call processorders();

回归正题

在这个例子中,增加了另一个名为t的变量(存储每个订单的合计)。此存储过程还在运行中创建了一个新表(如果它不存在的话),名为ordertotals。这个表将保存存储过程生成的结果。FETCH像以前一样取每个order_num,然后用CALL执行另一个存储过程(我们在前一章中创建)来计算每个订单的带税的合计(结果存储到t)。最后,用INSERT保存每个订单的订单号和合计。

此存储过程不返回数据,但它能够创建和填充另一个表,可以用一条简单的SELECT语句查看该表:

select * 
from ordertotals;

输出如下:

img

插句题外话

对于这个输出结果,如果好奇为什么最后两行是重复的,以及想改正这一点,可以看看这篇博客 《MySQL必知必会》第24章 使用游标 中的bug:最后一行被重复INSERT

回归正题

这样,我们就得到了存储过程、游标、逐行处理以及存储过程调用其他存储过程的一个完整的工作样例。

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

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

相关文章

Python clickhouse-driver 类库使用学习总结

实践环境 python3 .9.13 clickhouse-driver 0.2.9 实践操作 # -*- coding:utf-8 -*-import clickhouse_driverif __name__ == __main__:host = 192.168.88.131port = 9000 # 注意,不能使用默认的8123username = testaccpassword = test1234database = default# 连接方式1# con…

随波逐流工具使用_Week1

跟着大师傅的公众号做题的week1 来源以及说明 (文章主要是了解怎样使用长弓三皮大师傅的随波逐流工具,wp以及附件来自大师傅长弓三皮) (这周主要是做笔记的软件老是出现问题,有一些笔记有点乱,后面慢慢改进) 软件及题目下载 http://www.1o1o.xyz/bo_softdown.html CTF题目wr…

开源 - Ideal库 - 特殊时间扩展方法(三)

分享特殊时间获取的扩展方法,包括当天开始/结束时间、当前周/月/季度/年的第一天和最后一天等,附代码示例和单元测试,库将上传至Nuget,源码在代码库。书接上回,我们继续来分享一些关于特殊时间获取的常用扩展方法。01、获取当天的开始时间 当天的开始时间指00:00:00时刻,…

Vue2: v-for 联合 ref 使用, 对应引用信息为数组

MarkTime: 2024-06-03 00:21:47 LogTime: 2024-11-10 23:25:35说明问题:调用ref引用的时候, 好奇获取的时候为什么 有的返回的是对象, 有的返回的是数组 (如下图)版本:vue: 2.6.14源码 <!--已经省略掉大部分代码 只保留结构为说明 --> <template><div class=&qu…

Debug: setTimeout 使用做定时器时的错误函数传递方式

MarkTime: 2024-05-24 10:41:26 LogTime: 2024-11-10 14:55:53首先复习 setTimeout(): 语法: let timeId = setTimeout(func|code, [delay_millisecond]) 说明: 延时器. 延迟delay_millisecond后, 执行参数1setInterval(): 语法: let timeId = setInterval(func|code, [delay_…

Debug: calc() 未生效 = 内嵌样式表达式需使用空格分隔

MarkTime: 2024-06-25 17:10 LogTime: 2024-11-09 13:58:02结论calc()内的表达式 需要使用 空格分隔符, 即 更正 calc(100%-100px-10px); => calc(100% - 100px - 10px); 即可源<!-- 原错误代码 --> <div class="layout-search" style="width: 100%…

[云研发]腾讯云cloudstudio使用教程-记录1

cloudstudio在vscode中新建终端,使用nvidia-smi查看显卡与gpu情况,可知当前使用的显卡是T4的,显存有15GTalk is cheap. Show me the code

rip

题目链接:rip。 IDA 打开附件,进入到 main 函数,反编译后如下。留意到存在 gets 函数,因此存在栈溢出漏洞,经过动态调试,确定在输入 0x17 个字符后,即可覆盖到返回地址。同时,留意到存在 fun 函数,如下。因此,直接将程序执行流劫持到 fun 函数,即可 GetShell(此处需…

test_your_nc

题目链接:test_your_nc。 打开附件,进入 main 函数,IDA 反编译如下。因此直接连接即可 GetShell。

【docker应用】从Docker(k8s)镜像或者容器里提取jar包

引言 在docker或者k8s上运行了第三方的应用,很多时候我们都想知道运行流程,一般都会docker或者k8s容器中获取jar包。 那么该如何把里面的jar包提取出来呢? 其实有两种方式,分别为: 从镜像信息获取 进入容器获取从镜像信息获取 首先,查看镜像: docker image ls |grep <…

充电桩普遍选用芯海CSE7761集成3路ADC的免校准单相计量芯片

CSE7761是一款单相多功能电能计量芯片,工作于 3.579MHz 时钟频率,其内部集成了 3 路 sigmadelta ADC、功率计算器、能量-频率转换器、一路 SPI 接口、一路 UART 接口。适用于充电桩、单相多功能电能表、智能插座、智能家电等应用.CSE7761 设计用于精确计算电压有效值、电流有…

喝个水也要整智能家居?

ChatGPT对文章的摘要 这篇博客讲述了作者在家庭日常用水管理方面的改进经历。由于家里用水量增多,作者不断优化解决方案,从最初用农夫山泉桶装水,到购买反渗透净水机,再到通过智能化手段 简化补水流程。过程中,作者不断提高设备自动化程度,最终实现了全自动化补水,并且详…