MySQL Cursor怎么用?新手也能看懂的逐行处理数据指南

ChatGPT2026-04-27 10:12:2821

温馨提示:在 ChatGPT 官网(www.chatgpt.com)使用 GPT-5.5、ChatGPT-Image-2 等模型时,需要 ChatGPT Plus 或更高等级的会员权限。如需购买账号或充值会员,请扫码添加我们客服咨询。

MySQL游标(Cursor)用于逐行处理查询结果集,特别适合需要对每一行进行单独操作的场景,使用游标通常分四步:**声明**(DECLARE)、**打开**(OPEN)、**提取**(FETCH)、**关闭**(CLOSE),先通过DECLARE cursor_name CURSOR FOR SELECT语句定义游标,OPEN开启,在循环中执行FETCH...INTO将当前行数据存入变量,配合NOT FOUND处理程序跳出循环,处理完所有行后,使用CLOSE释放游标,注意游标会占用内存,用完务必关闭,存储过程中可结合DECLARE CONTINUE HANDLER`自动捕获结束标志,避免无限循环。

本文目录导读:

  1. 第一步:先弄一个“原材料”
  2. 第二步:开始写Cursor的“四部曲”
  3. 我们一行一行地解释一下上面的代码
  4. 怎么运行这个存储过程?
  5. 给小白用户的几个建议
  6. 总结一下

你好,我是教程网的编辑,今天我们来聊一个听起来有点吓人,但其实用起来很简单的概念:MySQL里的Cursor(游标)。

很多刚学数据库的朋友,一开始接触的都是“批量”操作,比如你用 SELECT 查出100条数据,或者用 UPDATE 一次性修改1000行数据,这些操作都是“一下子”处理完一堆数据。

有时候你会遇到一个情况:你不想一下子处理那么多数据,你想一条一条地看,一条一条地处理,你查出了某个用户的订单,你想第一个订单给它打个折,第二个订单维持原价,第三个订单送个赠品,这种“每条数据都要单独判断、单独操作”的情况,普通SQL语句就很难办了。

这时候,就用到了Cursor,简单说,Cursor就像是一个“数据指针”或者“书签”,你可以把这个指针放在你查询出来的结果集(很多条数据)的上面,然后一步一步往下走,每走一步,就拿起当前这一条数据,看看它、用它、或者修改它。

它最大的好处就是让你能“逐行”处理数据,而不是“整批”处理。

下面,我会用一个超级简单的例子,一步步教你如何在MySQL里写一个Cursor,我们不谈那些复杂的理论,只说“怎么做”和“为什么这么做”。

第一步:先弄一个“原材料”

要处理数据,你得先有数据,假设我们有一个叫 student 的表,专门记录学生的考试成绩,我们先创建这个表,再往里面填几条数据。

-- 创建一个叫 student 的表
CREATE TABLE student (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50),
    score INT
);
-- 往表里插入几条学生成绩
INSERT INTO student (name, score) VALUES ('小明', 85);
INSERT INTO student (name, score) VALUES ('小红', 59);
INSERT INTO student (name, score) VALUES ('小刚', 72);
INSERT INTO student (name, score) VALUES ('小丽', 45);
INSERT INTO student (name, score) VALUES ('小强', 93);

好了,现在我们的 student 表里有了5位同学,我们现在想做什么呢?我们想给每个不及格(低于60分)的同学加10分“鼓励分”。

这件事,用一条 UPDATE 语句也能做,但为了演示Cursor,我们就用Cursor来一条一条地处理。

第二步:开始写Cursor的“四部曲”

在MySQL里,写Cursor就像是在执行一套固定的流程,总共分四步:声明、打开、抓取、关闭,记住这个顺序,你就能写出来。

  1. 声明 (Declare):告诉MySQL,我要用一个Cursor了,这个Cursor要绑在哪条查询语句上。
  2. 打开 (Open):执行声明时的那条查询语句,把结果准备好,光标就位。
  3. 抓取 (Fetch):真正开始干活,把当前光标指向的那一行数据取出来,放到一个变量里,然后光标自动移动到下一行。
  4. 关闭 (Close):数据都处理完了,把Cursor关掉,释放掉占用的资源。

我们把这四步套入一个完整的“存储过程”里,存储过程你可以理解成一个“脚本”或者“小程序”,可以一次性执行多行命令。

-- 这段代码要全部复制到MySQL的命令行或者工具里执行
DELIMITER //
CREATE PROCEDURE add_encouraging_score()
BEGIN
    -- 声明变量:用来临时存放从数据库里取出来的“一行数据”
    DECLARE done INT DEFAULT 0;   -- 一个标记,用来判断是否已经拿完所有数据了
    DECLARE student_name VARCHAR(50);  -- 用来存放当前抓取到的学生名字
    DECLARE student_score INT;         -- 用来存放当前抓取到的学生分数
    -- 第一步:声明Cursor
    -- 这个cursor叫 score_cursor,它的结果集是:查询所有分数小于60的学生名字和分数
    DECLARE score_cursor CURSOR FOR 
        SELECT name, score FROM student WHERE score < 60;
    -- 关键的一行:设置一个“处理程序”,当cursor抓取不到数据时(也就是没数据了),就把 done 这个变量设为1
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
    -- 第二步:打开Cursor,开始干活
    OPEN score_cursor;
    -- 第三步:循环抓取数据
    read_loop: LOOP
        -- 从cursor里抓取当前行的数据,分别存到 student_name 和 student_score 变量里
        FETCH score_cursor INTO student_name, student_score;
        -- done 变成了1,说明已经把所有的数据都抓完了,就退出循环
        IF done = 1 THEN
            LEAVE read_loop;
        END IF;
        -- 这里就是你对“这一行”数据进行的操作
        -- 给当前这个不及格的学生加10分
        UPDATE student SET score = score + 10 WHERE name = student_name;
        -- 这里可以加入你的业务逻辑,比如打印一下处理了谁
        -- 直接打印出信息(在MySQL命令行里会看到输出)
        SELECT CONCAT('正在给 ', student_name, ' 加10分,原来的成绩是 ', student_score) AS info;
    END LOOP read_loop;
    -- 第四步:关闭Cursor
    CLOSE score_cursor;
END//
DELIMITER ;

我们一行一行地解释一下上面的代码

  • DELIMITER //DELIMITER ;:这是为了让MySQL知道,整个存储过程的代码结束于 ,而不是分号,因为存储过程里面有很多分号,MySQL默认看到分号就执行,这下会出乱子,所以我们先临时把“语句结束符”改成 。

  • CREATE PROCEDURE add_encouraging_score():创建一个叫 add_encouraging_score 的存储过程,括号是空的,表示这个“小程序”不需要额外输入。

  • BEGIN ... END:这就是“小程序”的代码范围。

  • DECLARE done INT DEFAULT 0;:声明一个叫 done 的变量,初始值是0,0代表“还没处理完”,1代表“处理完了”。

  • DECLARE ... CURSOR FOR ...:这就是声明我们的Cursor。score_cursor 这个名字你可以随便起,但最好见名知意。

  • DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;:这一行非常重要!很多人写Cursor卡住就是因为没有这一行,它的意思是:当 FETCH 命令再也抓不到任何数据的时候(也就是 NOT FOUND 错误),就自动把 done 变量的值设置为1,这样,我们的循环就知道该停止了,否则,程序会永远停在 FETCH 那一步,变成一个死循环。

  • OPEN score_cursor;:开门,把查询结果准备好。

  • read_loop: LOOP ... END LOOP read_loop;:开启一个循环。read_loop 是这个循环的名字,方便后面用 LEAVE 来跳出。

  • FETCH score_cursor INTO student_name, student_score;:抓取当前行的数据。INTO 后面的变量顺序,要和声明Cursor时的 SELECT name, score 顺序一模一样。

  • IF done = 1 THEN LEAVE read_loop; END IF;:检查 done 是否变成了1,如果是,就跳出循环,这是阻止程序跑飞的唯一条件。

  • UPDATE student SET score = score + 10 WHERE name = student_name;:这就是我们真正的业务逻辑,每次抓取到一个 student_namestudent_score,我们就用这个名字更新数据库,给他加10分。

  • SELECT CONCAT(...):这行是辅助信息,让你在运行工具里能看到程序在干嘛。CONCAT 是拼接字符串的函数。

  • CLOSE score_cursor;:关掉Cursor,释放内存。

怎么运行这个存储过程?

写完之后,保存一下,然后在你的MySQL工具里(比如Navicat、MySQL Workbench,或者命令行)输入:

CALL add_encouraging_score();

你就会看到工具里一条一条地打印出信息:“正在给小红加10分,原来的成绩是59”,“正在给小丽加10分,原来的成绩是45”,再去查 student 表,你会发现小红和小丽的成绩都已经增加了10分。

给小白用户的几个建议

  1. 不要怕,它只是一个“循环”:你可以把Cursor理解成编程语言里的 for each 循环,你有一个列表(查询结果),然后你一个接一个地处理列表里的东西。

  2. 记得写上那个“处理器”DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; 这一行,可以把它叫做“刹车”,没有刹车,你的Cursor循环就停不下来,程序会报错或者一直转圈。

  3. 变量顺序要对FETCH ... INTO 后面的变量顺序,必须完全和你声明Cursor时 SELECT 语句后面的字段顺序一致,如果你 SELECT name, scoreINTO 后面就必须先写一个存名字的变量,再写一个存分数的变量,顺序错了,数据就存乱了。

  4. 尽量少用Cursor:虽然我教你怎么用,但我还是要告诉你:在MySQL里,能不使用Cursor,就尽量不使用Cursor,为什么?因为它的速度比较慢,数据库最擅长处理“批量”操作,你用Cursor一条一条操作,就像是大炮打蚊子,效率很低,能用一条 UPDATEDELETE 解决的批量问题,就绝对不要用Cursor。

    • 不好的例子(用Cursor):给所有不及格的同学加10分,如上。
    • 好的例子(用SQL)UPDATE student SET score = score + 10 WHERE score < 60; 这一行就搞定,效率快100倍。
  5. 什么时候才用Cursor? 当你的逻辑确实非常复杂,无法用一条SQL语句表达时,你需要对每一行的数据都进行一个“带有复杂条件判断”的运算,然后根据这个运算结果,决定对这个数据是加、减、还是删除,这时候,Cursor就派上用场了。

总结一下

MySQL的Cursor不是一个复杂的东西,它只是一个让你能够“逐行”查看和处理数据的工具,记住它的四步流程:声明、打开、抓取、关闭,在抓取的时候,一定要配合一个“循环”和一个“退出循环”的条件(我们上面用的是 done 变量),写代码时,别搞错变量的顺序。

希望这篇文章能帮你搞懂Cursor是怎么回事,如果你在使用过程中遇到各种奇怪的错误,比如遇到购买账号会员/充值代充等问题,或者想找最新的AI工具资讯(比如ChatGPT, Claude, Midjourney, Gemini, Cursor, Suno等),都可以扫描页底二维码找本站咨询,我们一直在为你准备最实用的教程,今天就试着在你的练习数据库里跑一下上面的例子吧,你会发现自己很快就能上手。

温馨提示:在 ChatGPT 官网(www.chatgpt.com)使用 GPT-5.5、ChatGPT-Image-2 等模型时,需要 ChatGPT Plus 或更高等级的会员权限。如需购买账号或充值会员,请扫码添加我们客服咨询。

本文链接:https://www.lexitong.com/ai/420.html

游标逐行处理MySQLmysql cursor使用

相关文章

网友评论