ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

达梦数据库游标入门:从DMSQL声明到逐行取数的完整实践

达梦数据库游标入门:从DMSQL声明到逐行取数的完整实践 1. 达梦数据库游标到底是什么为什么逐行取数绕不开它刚接触达梦数据库DM8做 DMSQL 存储过程开发时很多人第一反应是用SELECT ... INTO把查询结果塞进变量。这个写法本身没问题但它有个硬限制只能返回一行。一旦查询命中多条记录数据库直接抛TOO_MANY_ROWS错误程序中断。我在做人员信息批量同步时就踩过这个坑一条按部门查员工的语句测试库只有一条数据跑得通上生产直接炸。达梦数据库游标CURSOR就是为解决多行结果集逐条处理而生的机制。你可以把它理解成一个带指针的结果集窗口查询语句执行后结果集被装进游标工作区指针停在第一行之前然后你通过FETCH一行一行往下拨每拨一次取一行数据做业务处理处理完再拨下一行直到取不到数据为止。达梦数据库的游标分两大类。静态游标在编译阶段就确定了关联的查询语句又细分为隐式游标和显式游标动态游标则是在声明时只定义游标变量真正打开时才用FOR子句绑定查询语句灵活性更高。游标和游标变量的关系类似常量和变量——静态游标声明即绑定动态游标声明时只是个空壳。这篇文章面向刚上手达梦数据库的开发者我会用一张人员表把显式游标的声明、打开、取值、关闭四个步骤完整走一遍再对照隐式游标的差异最后给出可直接复制的脚本和执行结果。你跟着敲一遍就能独立完成一次游标遍历验证。核心检索词先记住达梦数据库游标、DMSQL 显式游标、逐行取数。2. 环境准备与 TaoToken 前置配置让 DMSQL 调试更顺手在正式写游标脚本之前先把调试环境理顺。达梦数据库自带的 disql 命令行工具和 DM 管理工具都能跑 DMSQL 块但如果你想让 AI 辅助生成或排查游标脚本可以配一个模型调用入口。我平时用 TaoToken 做这类辅助它的 API 地址是https://taotoken.net/api官网在https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content。配置方式很简单以常见的 OpenAI 兼容客户端为例把 Base URL 指向 TaoToken 的 API 地址填入申请的 Key再选一个模型 ID 即可。下面是一份可直接复制的 JSON 配置片段路径按你本地客户端的 settings 文件位置来{ base_url: https://taotoken.net/api, api_key: sk-你的TaoToken密钥, model: claude-sonnet-4-20250514, timeout: 60 }如果你用的是 Cline 这类带 MCP 的编辑器插件配置里同样要写全三件套Base URL、Key、Model ID缺一个都会连不上。我试过只填 Key 不填 Base URL结果请求直接打到默认地址报local proxy failed排查了半天才发现是地址没改。需要说明的是TaoToken 在这里的角色是模型调用入口帮你生成游标模板、解释报错信息它不替代达梦数据库本身也不替代 disql 或 DM 管理工具。数据库连接、建表、执行 DMSQL 块还是在你本地的达梦实例上完成。配好之后你可以先在模型对话里让它帮你把一段游标逻辑翻译成 DMSQL 语法确认没问题再贴进数据库执行。这样比直接在数据库里反复试错效率高。如果你要长期做存储过程开发可以考虑 Coding Plan把常用的游标模板、异常处理片段沉淀下来后面复用会快很多。3. 可复制配置建表 显式游标完整脚本这一节是全文的核心给你一套能直接跑的脚本。先建一张人员表插入几条测试数据然后写一个显式游标把每行数据打印出来。建表和初始化数据-- 建表 CREATE TABLE PERSON.PERSON_TEST ( PERSONID INT, NAME VARCHAR(50), PHONE VARCHAR(50), DEPTID INT ); -- 插入测试数据 INSERT INTO PERSON.PERSON_TEST VALUES (1, 孙丽, 13800000001, 10); INSERT INTO PERSON.PERSON_TEST VALUES (2, 张伟, 13800000002, 10); INSERT INTO PERSON.PERSON_TEST VALUES (3, 李娜, 13800000003, 20); INSERT INTO PERSON.PERSON_TEST VALUES (4, 王强, 13800000004, 20); INSERT INTO PERSON.PERSON_TEST VALUES (5, 赵敏, 13800000005, 30); COMMIT;显式游标的完整四步脚本声明、打开、拨动、关闭都在里面DECLARE v_name VARCHAR(50); v_phone VARCHAR(50); -- 第一步声明游标绑定查询语句 CURSOR c_person IS SELECT NAME, PHONE FROM PERSON.PERSON_TEST WHERE DEPTID 10; BEGIN -- 第二步打开游标结果集装入工作区 OPEN c_person; -- 第三步循环拨动游标逐行取值 LOOP FETCH c_person INTO v_name, v_phone; EXIT WHEN c_person%NOTFOUND; -- 取不到数据时退出 PRINT 姓名 || v_name || 电话 || v_phone; END LOOP; -- 第四步关闭游标释放资源 CLOSE c_person; END; /这段脚本里几个关键点值得说清楚。CURSOR c_person IS SELECT ...是声明部分游标名后面跟IS再跟查询表达式这是达梦数据库显式游标最常见的写法。OPEN c_person执行查询并把指针定位到第一行之前。FETCH ... INTO把当前行数据赋给变量同时指针下移。c_person%NOTFOUND是游标属性最近一次拨动没取到数据时为 TRUE用它做循环退出条件最稳妥。最后CLOSE必须执行否则游标占用的工作区资源不会释放。如果你需要控制取数行数比如只处理前 5 行可以在循环里加EXIT WHEN c_person%ROWCOUNT 5;。%ROWCOUNT记录的是已经取到的元组数第一次拨动前为 0。对于数据量大的场景逐行 FETCH 效率偏低可以用批量取值DECLARE TYPE t_rec IS RECORD (v_name VARCHAR(50), v_phone VARCHAR(50)); TYPE t_tab IS TABLE OF t_rec INDEX BY INT; v_info t_tab; CURSOR c_person IS SELECT NAME, PHONE FROM PERSON.PERSON_TEST; BEGIN OPEN c_person; FETCH c_person BULK COLLECT INTO v_info LIMIT 100; CLOSE c_person; FOR i IN 1 .. v_info.COUNT LOOP PRINT v_info(i).v_name || || v_info(i).v_phone; END LOOP; END; /BULK COLLECT INTO一次把结果集批量赋给集合变量配合LIMIT 100限制每次取 100 行减少上下文切换开销。注意BULK COLLECT后面 INTO 的变量必须是集合类型且不支持索引类型为 VARCHAR 的索引表。4. 验证请求与成功结果对照执行输出确认游标跑通脚本写完怎么确认真的跑通了在 disql 里执行上面的显式游标块正常输出应该是姓名孙丽 电话13800000001 姓名张伟 电话13800000002因为查询条件限定了DEPTID 10只有两条记录所以输出两行。如果你把 WHERE 条件去掉五行数据会全部打印出来。这个对照很关键输出行数和你查询条件命中的行数一致说明游标遍历逻辑正确。再验证一下隐式游标的行为。隐式游标不需要你声明执行 DML 或SELECT ... INTO时数据库自动创建名字统一叫SQL。看这个例子BEGIN UPDATE PERSON.PERSON_TEST SET PHONE 13818882888 WHERE NAME 孙丽; IF SQL%NOTFOUND THEN PRINT 此人不存在; ELSE PRINT 已修改影响行数 || SQL%ROWCOUNT; END IF; END; /执行后输出已修改影响行数1。这里SQL%ROWCOUNT返回的是 UPDATE 影响的行数。注意隐式游标的%ISOPEN永远是 FALSE因为语句执行完系统自动关闭了游标你没法手动干预它的打开和关闭。显式游标和隐式游标的属性差异用一张表对照更清楚属性隐式游标显式游标%FOUND语句是否修改或查询到记录最近一次拨动是否取到数据未打开时抛异常%NOTFOUND语句是否未命中记录最近一次拨动是否未取到数据%ISOPEN永远为 FALSE打开时为 TRUE否则 FALSE%ROWCOUNTDML 影响行数或 SELECT INTO 返回行数已取到的元组数未打开时抛异常验证动态游标也不难声明时不绑定查询打开时用 FOR 指定DECLARE v_name VARCHAR(50); CURSOR c_dyn; BEGIN OPEN c_dyn FOR SELECT NAME FROM PERSON.PERSON_TEST WHERE DEPTID 20; LOOP FETCH c_dyn INTO v_name; EXIT WHEN c_dyn%NOTFOUND; PRINT v_name; END LOOP; CLOSE c_dyn; END; /输出李娜和王强。动态游标还能带参数用?占位OPEN 时用 USING 传值DECLARE v_name VARCHAR(50); CURSOR c_dyn; BEGIN OPEN c_dyn FOR SELECT NAME FROM PERSON.PERSON_TEST WHERE DEPTID ? USING 30; LOOP FETCH c_dyn INTO v_name; EXIT WHEN c_dyn%NOTFOUND; PRINT v_name; END LOOP; CLOSE c_dyn; END; /输出赵敏。参数个数和类型必须和?一一匹配否则报错。5. 本篇常见报错排查401、TOO_MANY_ROWS 与游标未打开游标脚本跑不通报错信息往往很直接关键是知道去哪找原因。下面几个是我实际遇到频率最高的。TOO_MANY_ROWS 错误。这个不是游标本身的错而是你用了SELECT ... INTO但查询返回多行。解决办法就是改用显式游标逐行处理。比如SELECT NAME INTO v_name FROM PERSON.PERSON_TEST;会直接报这个错因为表里有五行数据。游标未打开异常。在OPEN之前访问%FOUND、%NOTFOUND、%ROWCOUNT都会抛异常。我见过有人把EXIT WHEN c1%NOTFOUND写在OPEN前面结果直接报错。记住顺序先 OPEN再 FETCH再判断属性。401 鉴权失败。如果你在配置 TaoToken 辅助调试时遇到 401先检查 Key 是否填对、是否过期。Base URL 要写https://taotoken.net/api不要多加路径。Key 和 Base URL 不匹配也会 401。local proxy failed。这个报错通常出现在客户端配置里 Base URL 没改请求打到了本地默认代理地址。检查你的 settings 文件确认base_url指向的是 TaoToken 的 API 地址而不是localhost或空值。reading choices 报错。这多半是模型返回格式和客户端预期不一致检查 Model ID 是否填了客户端支持的模型名。有些客户端对模型名大小写敏感claude-sonnet-4-20250514和Claude-Sonnet-4-20250514可能表现不同。OAuth 相关报错。如果你用的是需要 OAuth 授权的客户端token 过期后会报这个。重新走一遍授权流程或者改用 API Key 方式接入。游标已关闭后继续 FETCH。CLOSE之后再FETCH会报错。确保CLOSE放在循环外面且循环内没有提前关闭游标。排查时建议把 DMSQL 块拆小先单独跑OPEN和一次FETCH确认能取到数据再补循环和退出条件。这样定位问题比一次性跑完整脚本快得多。6. 继续深入把游标用进真实业务与长期开发流游标跑通只是第一步真正有价值的是把它嵌进业务逻辑。比如批量更新人员电话可以声明一个游标遍历所有人员在循环里根据 DEPTID 做不同处理再用UPDATE回写。这种逐行处理在数据清洗、报表生成、跨表同步里很常见。性能上有个经验能用 SQL 集合操作解决的尽量别用游标。游标是逐行处理数据量大时开销明显。只有当业务逻辑必须逐行判断、或者需要调用存储过程逐条处理时才用游标。批量场景优先考虑BULK COLLECT减少交互次数。如果你要长期做达梦数据库的存储过程开发建议把常用的游标模板、异常处理片段、属性判断逻辑整理成自己的代码库。配合 TaoToken 的 Coding Plan可以让模型帮你快速生成变体、解释报错、补全异常分支省去反复查文档的时间。需要生成或调试脚本时直接在模型对话里贴报错信息让它给出修改建议比翻手册快。最后留一个实用技巧写游标循环时EXIT WHEN条件尽量用%NOTFOUND而不是%FOUND取反前者语义更清晰也不容易在边界情况出错。关闭游标前如果还想知道总共处理了多少行读一下%ROWCOUNT再CLOSE这个值在关闭后就取不到了。
返回列表