
1. Oracle Cursor 简单用法从声明到 FETCH 的完整实操Oracle 里的 Cursor游标本质上就是一块指向查询结果集的指针你可以把它理解成逐行读取数据的遥控器。当你需要一行一行处理查询结果而不是一次性把数据全部捞出来时Cursor 就是最顺手的工具。它适合谁适合写存储过程、函数、触发器的 PL/SQL 开发者尤其是做批量折扣分摊、订单明细遍历、状态机流转这类逐行判断再更新的场景。我这次要做的是把一段真实的changeSpecialDiscount存储过程拆开讲清楚Cursor 怎么声明、怎么 OPEN、怎么 FETCH、怎么 CLOSE以及循环里那些容易踩坑的地方。同时因为我在 Cursor 编辑器里写这些 SQL顺手把 Cursor 的 Base URL 指向了 TaoToken 的统一 Key/API 通道这样写 PL/SQL 时用 AI 补全、解释报错都走同一个入口不用来回切工具。下面把两件事都写清楚Oracle Cursor 的用法和 Cursor 编辑器的配置。先说清楚 Cursor 在 Oracle 语境下的两种含义避免混淆。第一种是数据库游标PL/SQL 里的CURSOR c1 IS SELECT ...这是本篇的技术主体。第二种是 Cursor 编辑器一个 AI 代码编辑器本篇要配置它的 Base URL。两者名字撞车但一个是 SQL 语法一个是 IDE 设置读的时候注意区分。我实测下来把编辑器接上统一通道后写存储过程时让 AI 解释FETCH c1 INTO ...的字段顺序、检查%NOTFOUND用法效率提升明显尤其是字段多的时候不用一个个数。这段存储过程的核心逻辑是根据传入的compID_in、ccID_in、coNO_in三个参数查出订单明细按每行的sp_per_unit_contr * qty_order占比把总折扣wspcl_disc分摊到每一行最后更新tbco_item的special_disc字段。Cursor 在这里的作用就是遍历明细行逐行计算分摊金额。下面从声明开始一步步拆。1.1 Cursor 的声明把查询结果集绑定到游标名声明游标就是告诉 Oracle我要执行这条 SELECT结果先别急着给我挂在一个叫 c1 的名字下面。语法结构是CURSOR 游标名 IS SELECT 语句。在changeSpecialDiscount里声明部分长这样CURSOR c1 IS SELECT ITEM_NO, COST_CC_CONTR, QTY_ORDER, LP_CUST_CONTR, STATUS, SRCE_TYPE, SP_PER_UNIT_CONTR FROM tbco_item WHERE COMP_ID compID_in AND CC_ID ccID_in AND CO_NO coNO_in;这里有几个细节值得说。第一游标声明里的 WHERE 条件直接引用了存储过程的入参compID_in、ccID_in、coNO_in这是合法的因为游标在 OPEN 时才真正绑定变量值。第二SELECT 的字段顺序必须和后面 FETCH 的变量顺序严格一致这是最容易出错的地方。第三游标声明只是定义此时并不会执行查询也不会占用数据库资源真正干活是在 OPEN 的时候。我踩过的坑有一次字段顺序写反了QTY_ORDER和LP_CUST_CONTR位置对调结果数量被当成金额算折扣分摊全乱但 SQL 本身不报错因为类型兼容。所以声明完游标最好把 SELECT 字段和 FETCH 变量列个对照表逐个核对。SELECT 字段FETCH 目标变量类型ITEM_NOwitem_noVARCHAR2(4)COST_CC_CONTRwcost_ccNUMBER(14,4)QTY_ORDERwqty_orderNUMBER(14,4)LP_CUST_CONTRwlp_contrNUMBER(14,4)STATUSwstatusVARCHAR2(4)SRCE_TYPEwsrce_typeVARCHAR2(1)SP_PER_UNIT_CONTRwsp_per_unit_contrNUMBER(14,4)这张表建议你在写游标时随手画一份字段一多肉眼核对很容易漏。声明阶段还有一点游标可以带参数比如CURSOR c1(p_comp VARCHAR2) IS SELECT ... WHERE COMP_ID p_comp这样更灵活但本篇的写法是直接引用外部变量两种都行看团队规范。1.2 OPEN、FETCH、CLOSE游标的三段式生命周期游标的使用遵循固定节奏OPEN 打开FETCH 取行CLOSE 关闭。这三步缺一不可尤其是 CLOSE忘了关会导致游标泄漏长时间运行会耗尽OPEN_CURSORS参数限制。OPEN 的写法很简单OPEN c1;执行到这一句Oracle 才真正执行游标里的 SELECT把结果集准备好指针停在第一行之前。此时如果查询很慢卡顿就发生在 OPEN 这一步而不是声明。FETCH 是逐行读取FETCH c1 INTO witem_no, wcost_cc, wqty_order, wlp_contr, wstatus, wsrce_type, wsp_per_unit_contr;每执行一次 FETCH指针下移一行把当前行的字段值塞进对应的变量。如果取不到行到底了变量值保持不变同时c1%NOTFOUND变成 TRUE。这里要注意FETCH 本身不报错取不到就是取不到你得自己判断。CLOSE 收尾CLOSE c1;关闭后游标占用的资源释放结果集失效。如果还想再遍历一遍必须重新 OPEN。在changeSpecialDiscount里作者用的是FOR idx IN 1..cnt_i LOOP ... END LOOP这种计数循环配合 FETCH而不是更常见的LOOP ... EXIT WHEN c1%NOTFOUND。这两种写法有区别计数循环依赖cnt_i明细总行数和游标返回行数完全一致如果中途有行被过滤掉FETCH 次数和实际行数对不上就会出问题。更稳妥的写法是用%NOTFOUND控制退出OPEN c1; LOOP FETCH c1 INTO witem_no, wcost_cc, wqty_order, wlp_contr, wstatus, wsrce_type, wsp_per_unit_contr; EXIT WHEN c1%NOTFOUND; -- 逐行处理逻辑 END LOOP; CLOSE c1;我建议你优先用%NOTFOUND版本它对数据变化更鲁棒。计数循环适合你非常确定行数稳定的场景但生产环境里数据随时可能变别赌。1.3 循环体内的分摊逻辑与 UPDATE 落库FETCH 拿到一行后循环体做三件事查状态码、算分摊金额、更新明细行。先看状态码查询SELECT ACTIVITY_CODE INTO act_cd FROM TBCM_STATUS WHERE SYSTEM_CODE CO AND TABLE_LEVEL 2 AND DATA_TYPE wsrce_type AND (STATUS_NAME1 wstatus OR STATUS_NAME2 wstatus);这里用wsrce_type和wstatus两个游标变量去查状态表拿到act_cd。注意这个 SELECT INTO 在循环里执行如果查不到会抛NO_DATA_FOUND如果查到多行会抛TOO_MANY_ROWS。生产代码里最好加异常处理或者用MAX(ACTIVITY_CODE)兜底。分摊逻辑是核心IF wsp_per_unit_contr 0 OR act_cd 15 THEN wsp_disc : 0; ELSIF cnt2 cnt_u THEN wsp_disc : ROUND(wspcl_disc * (wsp_per_unit_contr * wqty_order / sum_cc_all), 2); cnt2 : cnt2 1; ELSIF cnt2 cnt_u THEN wsp_disc : wspcl_disc - tot_disc; END IF;翻译一下如果单价为 0 或者状态码是 15这行不分摊给 0。否则按占比算cnt2是已分摊行计数器cnt_u是有效行数。最后一行用wspcl_disc - tot_disc兜底把四舍五入的误差补上保证分摊总额等于总折扣。这个最后一行兜底是财务类分摊的经典手法不然 ROUND 累积误差会让总额对不上。更新落库UPDATE tbco_item SET special_disc ROUND(wsp_disc, 2), date_modify SYSDATE WHERE COMP_ID compID_in AND CC_ID ccID_in AND CO_NO coNO_in AND item_no witem_no;用游标里的witem_no精确定位行逐行更新。这里没有 COMMIT说明事务控制交给调用方这是存储过程的常见约定。1.4 在 Cursor 编辑器里把 Base URL 指向 TaoToken写 PL/SQL 时我用的编辑器是 Cursor它的 AI 功能默认走官方通道。我想统一走 TaoToken 的 Key/API 通道这样模型调用集中管理。配置入口在 Cursor 的设置里找到模型配置部分把 Base URL 和 API Key 填进去。TaoToken 的 API 地址是https://taotoken.net/api注意这个地址不带任何查询参数。配置片段JSON 形式路径对应 Cursor 的 settings{ openai.baseUrl: https://taotoken.net/api, openai.apiKey: 你的TaoToken Key, openai.model: claude-sonnet-4-20250514 }三件套必须齐全Base URL、Key、Model ID。少一个都会报错。Model ID 按你在 TaoToken 控制台看到的可用模型填别照抄我的以实际为准。填完后重启 Cursor或者重新加载窗口让配置生效。如果你用的是 Claude Code 这类命令行工具配置方式不同走的是环境变量或配置文件。但 Cursor 编辑器就是上面这个 JSON 结构。我实测下来改完 Base URL 后AI 补全和对话都正常响应速度取决于所选模型。1.5 验证请求确认配置生效与 FETCH 结果正确配置改完先验证通道通不通。在 Cursor 里随便问一句比如解释一下 Oracle 游标 %NOTFOUND 的用法如果正常返回说明 Base URL 和 Key 生效。如果报 401说明 Key 不对如果报连接失败检查 Base URL 有没有多写斜杠或路径。数据库这边验证游标逻辑是否正确最直接的办法是单独跑一遍查询看行数和分摊结果。先确认游标返回的行数SELECT COUNT(*) FROM tbco_item WHERE COMP_ID 你的compID AND CC_ID 你的ccID AND CO_NO 你的coNO;把这个数字和存储过程里的cnt_i对比应该一致。再检查分摊后的明细SELECT ITEM_NO, SP_PER_UNIT_CONTR, QTY_ORDER, SPECIAL_DISC FROM tbco_item WHERE COMP_ID 你的compID AND CC_ID 你的ccID AND CO_NO 你的coNO ORDER BY ITEM_NO;把所有行的SPECIAL_DISC加起来应该等于tbco_head里的DISC_AMT。如果对不上多半是最后一行兜底逻辑没生效或者cnt_u和cnt_i的统计口径不一致。我实测时遇到过一次总额差 0.01就是 ROUND 误差没兜住检查后发现cnt2的递增条件写错了。1.6 常见报错排查从 ORA-01001 到字段错位游标相关的报错有几个高频的对照着查。ORA-01001: invalid cursor游标没 OPEN 就 FETCH或者已经 CLOSE 了还在 FETCH。检查 OPEN 和 CLOSE 的位置别在循环里误关。ORA-01002: fetch out of sequence通常是 FOR UPDATE 游标在 COMMIT 之后继续 FETCH。如果你在循环里 COMMIT要么改成批量提交要么别用 FOR UPDATE。ORA-06502: PL/SQL: numeric or value errorFETCH INTO 的变量类型和字段不匹配或者变量长度不够。比如witem_no VARCHAR2(4)但实际 ITEM_NO 有 6 位就会截断报错。检查变量声明。ORA-01403: no data found循环里的 SELECT INTO 没查到数据。给状态码查询加异常处理或者用MAX()聚合避免空结果。ORA-01422: exact fetch returns more than requested number of rowsSELECT INTO 查到多行。检查 WHERE 条件是否唯一。编辑器这边如果 AI 请求报local proxy failed或reading choices之类的错误先确认 Base URL 是不是https://taotoken.net/apiKey 有没有过期Model ID 是否在可用列表里。OAuth 类报错通常出现在命令行工具Cursor 编辑器走的是 Key 认证不太会遇到。1.7 把两件事串起来统一通道 游标调试把 Cursor 编辑器的 Base URL 指向 TaoToken 后我写 PL/SQL 的流程变成在编辑器里写游标逻辑遇到%NOTFOUND用法不确定直接问 AI报错了把错误码贴进去让 AI 解释字段顺序拿不准让 AI 帮我列对照表。数据库那边用 SQL Developer 或 sqlplus 跑验证查询。两边配合调试效率比纯手工高不少。如果你也想统一管理模型调用可以先把 Cursor 的配置改好再去 TaoToken 控制台确认 Key 和可用模型。配置片段就是上面那段 JSON路径和字段名以你本地 Cursor 版本为准。数据库游标部分建议从%NOTFOUND版本练起把计数循环当进阶用法。分摊逻辑里的最后一行兜底是财务场景的必备技巧记住它。需要拿 Key 或看接入文档走这两个入口API Keys 在https://taotoken.net/api-keys接入文档在https://taotoken.net/doc。想先试试模型对话效果去https://taotoken.net/chat。长期写代码、跑 Agent 的话Coding Plan 在https://taotoken.net/coding-plan。这些地址都带统一来源标识方便你回溯。最后留一个实用技巧游标调试时在 FETCH 后面加一句DBMS_OUTPUT.PUT_LINE(witem_no || : || wsp_disc);把每行的中间结果打出来比事后查表快得多。记得先SET SERVEROUTPUT ON。这个习惯帮我省了很多次来回查数据的时间。