MySQL游标(Cursor)详解及实际应用:从声明到循环取数的完整实践 1. 为什么集合操作搞不定时MySQL 游标Cursor才是那把手术刀先说结论MySQL 游标Cursor是存储过程里用来逐行处理结果集的机制它让你像用指针扫描表格一样一行一行地读数据、做判断、再写回去。适合谁适合那些业务逻辑没法用一句UPDATE或INSERT ... SELECT摆平的场景比如行与行之间有依赖的累计计算、需要按行调用不同分支的清洗规则、或者要把大批量操作拆成小批次降低锁竞争。我见过太多人一上来就写游标结果几百万行数据跑了几十分钟最后发现其实一句窗口函数就能解决。所以这篇不是劝你用游标而是帮你搞清楚什么时候必须用它怎么声明、打开、逐行读取、关闭以及踩坑时怎么排查。游标本质上是数据库为用户开的一块数据缓冲区存放SELECT的执行结果并维护一个指向当前行的指针。MySQL 里分两类隐式游标由系统内部自动管理你感知不到显式游标则是你在存储过程或函数里手动DECLARE、OPEN、FETCH、CLOSE的那种。本文聚焦显式游标因为只有它需要你写代码控制。一个完整的游标流程遵循四步声明时只绑定查询语句但不执行打开时才真正执行查询并把结果集装进游标获取时把当前行塞进变量并自动下移指针关闭时释放资源。听起来简单但真正写起来NOT FOUND处理程序、声明顺序、嵌套游标的状态标志每一个都能让你调试半天。下面我会用一个可复制的建表脚本和存储过程把整条链路走通再给出执行验证和常见报错排查。你跟着敲一遍基本就能在自己的业务里改着用了。2. 前置准备建表脚本与 TaoToken 环境接入在写游标之前得先把实验环境搭好。我准备了一张employees表字段简单但足够演示逐行逻辑员工 ID、姓名、部门、月薪、入职日期。建表脚本如下直接复制到 MySQL 客户端执行即可。CREATE DATABASE IF NOT EXISTS cursor_demo DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; USE cursor_demo; DROP TABLE IF EXISTS employees; CREATE TABLE employees ( emp_id INT PRIMARY KEY AUTO_INCREMENT, emp_name VARCHAR(64) NOT NULL, department VARCHAR(64) NOT NULL, salary DECIMAL(10,2) NOT NULL DEFAULT 0.00, hire_date DATE NOT NULL, bonus DECIMAL(10,2) NOT NULL DEFAULT 0.00, KEY idx_department (department), KEY idx_hire_date (hire_date) ) ENGINEInnoDB; INSERT INTO employees (emp_name, department, salary, hire_date) VALUES (张伟, 研发, 18000.00, 2021-03-15), (李娜, 研发, 22000.00, 2020-07-01), (王强, 销售, 12000.00, 2022-01-10), (赵敏, 销售, 15000.00, 2019-11-20), (陈晨, 运营, 11000.00, 2023-05-08), (刘洋, 运营, 13500.00, 2022-09-30);如果你平时是在本地客户端连数据库那到这一步就够了。但如果你像我一样习惯把 AI 辅助编码工具接进来让模型帮你生成或审查存储过程那可以顺手把 TaoToken 的接入配好。它的作用是把模型调用统一到一个入口方便你在写 SQL、调存储过程时随时让模型帮你检查语法和逻辑。TaoToken 的 API 地址是https://taotoken.net/api官网入口在https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content。如果你用的是 Claude Code 这类工具可以在配置里指定 Base URL 和 Key如果只是想验证模型能不能正常对话直接进模型对话页面发一条消息就行。需要长期跑编码任务或 Agent 的可以看 Coding Plan它更适合高频调用。这里要强调一点TaoToken 是模型调用的接入层不是数据库代理也不替代你的 MySQL 客户端。你的 SQL 还是在本地或服务器上的 MySQL 里执行TaoToken 只是帮你把 AI 能力接进来辅助你写代码、查报错。两者分工明确别混在一起。环境准备好之后先确认表里有数据SELECT COUNT(*) AS total FROM employees;返回 6 就说明数据到位了。接下来进入正题写第一个游标存储过程。3. 可复制配置从声明到关闭的完整存储过程这一节是全文的核心我会给出一个完整的存储过程它遍历employees表根据部门和薪资计算每个人的奖金并更新回表。这个逻辑用集合操作也能做但为了演示游标的逐行控制我们故意用游标实现。先看完整代码再逐段拆解。USE cursor_demo; DROP PROCEDURE IF EXISTS calc_bonus_by_cursor; DELIMITER // CREATE PROCEDURE calc_bonus_by_cursor() BEGIN -- 1. 声明变量 DECLARE v_done INT DEFAULT 0; DECLARE v_emp_id INT; DECLARE v_emp_name VARCHAR(64); DECLARE v_department VARCHAR(64); DECLARE v_salary DECIMAL(10,2); DECLARE v_bonus DECIMAL(10,2); -- 2. 声明游标绑定查询语句 DECLARE emp_cursor CURSOR FOR SELECT emp_id, emp_name, department, salary FROM employees WHERE salary 0 ORDER BY emp_id; -- 3. 声明 NOT FOUND 处理程序读完置标志位 DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done 1; -- 4. 打开游标 OPEN emp_cursor; -- 5. 循环逐行读取 read_loop: LOOP FETCH emp_cursor INTO v_emp_id, v_emp_name, v_department, v_salary; IF v_done 1 THEN LEAVE read_loop; END IF; -- 6. 逐行业务逻辑按部门系数计算奖金 IF v_department 研发 THEN SET v_bonus v_salary * 0.20; ELSEIF v_department 销售 THEN SET v_bonus v_salary * 0.15; ELSE SET v_bonus v_salary * 0.10; END IF; UPDATE employees SET bonus v_bonus WHERE emp_id v_emp_id; END LOOP; -- 7. 关闭游标 CLOSE emp_cursor; END // DELIMITER ;这段代码里有几个关键点我逐个说明。声明顺序不能乱。MySQL 要求所有DECLARE必须放在可执行语句之前而且顺序是变量 → 条件 → 游标 → 处理程序。如果你把DECLARE CONTINUE HANDLER写在DECLARE CURSOR前面会直接报语法错误。我试过把 handler 提前结果 MySQL 报ERROR 1337 (42000): Variable or condition declaration after cursor or handler declaration排查了好一会儿才反应过来是顺序问题。NOT FOUND处理程序的作用是当FETCH没有更多行可读时把v_done置为 1。注意它是CONTINUE类型意味着触发后继续执行后面的语句而不是退出。所以循环里必须紧接着判断v_done否则会死循环。FETCH语句的变量个数必须和游标SELECT的列数、类型一一对应。上面SELECT了四列FETCH就接四个变量顺序也要一致。如果列数对不上MySQL 会报ERROR 1328 (HY000): Incorrect number of FETCH variables。循环标签read_loop:是可选的但建议加上尤其是嵌套游标时LEAVE read_loop能明确指定跳出哪一层。如果你用 AI 辅助工具来审查这段代码可以把存储过程贴给模型让它帮你检查声明顺序和变量类型。TaoToken 的模型对话入口可以直接发代码Coding Plan 则适合把这类检查集成到日常编码流程里。API Keys 在控制台生成接入文档里有各语言示例按需取用。代码写完后执行CALL calc_bonus_by_cursor();就能跑起来。下一节验证结果。4. 验证请求与成功结果执行存储过程并核对数据存储过程创建成功后先确认它存在SHOW PROCEDURE STATUS WHERE Db cursor_demo;能看到calc_bonus_by_cursor就说明创建成功。然后执行CALL calc_bonus_by_cursor();如果没有任何报错说明游标跑完了。接着查表验证奖金是否按预期写入SELECT emp_id, emp_name, department, salary, bonus FROM employees ORDER BY emp_id;预期结果应该是研发部门两人奖金为薪资的 20%销售部门两人为 15%运营部门两人为 10%。比如张伟薪资 18000奖金应为 3600王强薪资 12000奖金应为 1800。你可以用下面这句直接核对计算是否正确SELECT emp_id, emp_name, department, salary, bonus, ROUND(salary * CASE department WHEN 研发 THEN 0.20 WHEN 销售 THEN 0.15 ELSE 0.10 END, 2) AS expected_bonus FROM employees ORDER BY emp_id;如果bonus和expected_bonus两列完全一致说明游标逐行处理逻辑正确。这一步很关键因为游标最容易出的问题不是语法错而是逻辑错——比如done标志没重置、FETCH变量错位导致部分行被跳过或重复处理。再跑一次CALL会怎样因为UPDATE是幂等的每次都用薪资重新算奖金所以重复执行结果不变。但如果你的业务逻辑是累加型的比如bonus bonus 100那重复执行就会翻倍。这也是游标使用时要特别注意的地方明确你的操作是否可重复执行。验证通过后可以再测一个边界场景把WHERE salary 0改成WHERE salary 999999让游标查不到任何行。这时FETCH第一次就会触发NOT FOUNDv_done置 1循环直接LEAVE不会报错也不会更新任何数据。这个测试能帮你确认空结果集时游标不会卡死。到这里一个完整的游标流程就跑通了。但实际开发中报错才是常态。下一节整理几个高频错误和排查动作。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth 对照游标本身的报错大多集中在语法和逻辑层面但如果你在接入 AI 辅助工具时遇到问题报错信息可能来自另一条链路。我把两类都列出来方便你对照。先说 MySQL 游标相关的真实报错。ERROR 1337 (42000): Variable or condition declaration after cursor or handler declaration。这是声明顺序错了。解决办法把所有DECLARE变量放最前面然后DECLARE CURSOR最后DECLARE HANDLER。记住口诀变量 → 条件 → 游标 → 处理程序。ERROR 1328 (HY000): Incorrect number of FETCH variables。FETCH的变量个数和游标SELECT的列数不一致。检查SELECT了几列FETCH INTO就写几个变量类型也要匹配。ERROR 1329 (02000): No data - zero rows fetched, selected, or processed。这个通常出现在没有正确声明NOT FOUND处理程序时FETCH读到末尾直接抛异常。加上DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done 1;就能解决。ERROR 1305 (42000): PROCEDURE cursor_demo.calc_bonus_by_cursor does not exist。存储过程没创建成功或者当前数据库选错了。先USE cursor_demo;再CALL。再说 AI 工具接入侧的报错。如果你在配置 TaoToken 时遇到401 Unauthorized通常是 API Key 没填对或已失效去控制台重新生成一个确认请求头里的Authorization: Bearer key格式正确。遇到local proxy failed说明本地代理配置有问题检查你的工具是否误设了代理地址把它清掉或指向正确入口。遇到reading choices这类报错多半是响应体解析失败确认你调用的模型 ID 和接口路径匹配接入文档里有标准请求示例。遇到OAuth相关提示说明你在用需要授权登录的方式按文档走授权流程即可别手动拼 token。这里要提醒一句TaoToken 的 Base URL 是https://taotoken.net/apiKey 在控制台的 API Keys 页面生成。如果你用的是 Claude Code 或 Cline MCP 这类工具配置里要同时写全三件套Base URL、Key、Model ID。少任何一个都会连不上。Codex 的auth.json也是同理字段名按文档填别自己猜。排查思路总结成一句话先确认 MySQL 侧语法和逻辑没问题再确认 AI 工具侧的地址、Key、模型 ID 三件套齐全。两边分开查别混在一起调。6. 语义一致 CTA把游标写进你的真实业务游标这东西看一遍示例觉得简单真到自己业务里写往往卡在“什么时候该用、什么时候不该用”。我的经验是能用集合操作就用集合操作游标是最后的选择。但一旦确定要用就把它写规范——声明顺序、NOT FOUND处理、变量类型对齐、循环内逻辑尽量精简这四点做到基本不会出大问题。如果你在写存储过程时需要 AI 帮你审查语法或生成测试数据可以走 TaoToken 的模型对话入口直接贴代码问需要长期在编码工具里集成模型能力的看 Coding PlanAPI Key 和接入细节在控制台和接入文档里都有。地址统一从https://taotoken.net/api进官网入口带 UTM 的那个链接也能到。最后留一个实用技巧调试游标时别急着UPDATE真实表先把循环内的业务逻辑换成SELECT v_emp_id, v_emp_name, v_bonus;把每行计算结果打印出来看一遍。确认逻辑对了再换成写操作。这个习惯能帮你省下大量回滚和排查的时间。