ARTICLE DETAIL

资讯详情

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

Oracle 中 cursor、refcursor 与 sys_refcursor 的区别:用 TaoToken 统一 Key 跑通三类游标示例

Oracle 中 cursor、refcursor 与 sys_refcursor 的区别:用 TaoToken 统一 Key 跑通三类游标示例 1. 三类游标到底差在哪从一段真实报错说起先抛一个我见过很多次的场景。你在 PL/SQL 里写了个存储过程想把一张表的结果集返回给上层应用于是声明了一个cursor结果编译直接报PLS-00382: expression is of wrong type或者更隐蔽一点过程能建但调用时ORA-06550一路飘红。问题往往不在 SQL 本身而在于你把「静态游标」和「引用游标」的语义搞混了。Oracle 里的游标本质是「指向查询结果集的一个句柄」。按声明方式和生命周期可以分成三类显式 cursor、隐式 cursor、以及 REF CURSOR其中SYS_REFCURSOR是系统预定义的弱类型引用游标。它们最核心的区别在于结果集能不能跨程序边界传递。显式和隐式游标是 PL/SQL 内部的私有变量出了这个块就没了而 REF CURSOR 是一个「指针的指针」可以把结果集的读取权交给客户端或另一个子程序。这篇文章适合谁适合已经会写基本 PL/SQL、但在「什么时候用 cursor、什么时候必须用 sys_refcursor」上反复踩坑的开发者。我会给出可复制的建表脚本、三类游标的完整示例、逐条对比查询并说明怎么用 TaoToken 统一 Key 在 AI 工具里生成和校验这些脚本最后用真实执行输出验证行为差异。核心检索词就是Oracle cursor、refcursor、sys_refcursor 的区别与适用场景。先说结论方便你带着判断往下读能用隐式游标就别写显式能用静态 SQL 就别上 REF CURSOR只有当结果集必须返回给客户端、或在多个子程序间共享时才动用 REF CURSOR / SYS_REFCURSOR。这个优先级背后是效率和维护成本的权衡后面会用代码逐条印证。2. 用 TaoToken 统一 Key 准备 AI 校验环境写这类游标脚本最容易出错的地方是语法细节open ... for后面能不能跟变量、强类型 REF CURSOR 的return子句要不要和记录类型严格对齐、%ROWCOUNT在隐式游标里到底统计的是哪条语句。这些细节靠记忆很容易翻车我习惯让 AI 工具帮我生成初稿再逐条核对。但多个 AI 工具各自要配 Key、切模型管理起来很烦所以我用 TaoToken 把 Key 和 API 通道统一起来。TaoToken 在这里扮演的角色是「统一的模型接入层」你拿到一个 Key就能在支持自定义 Base URL 的 AI 工具里调用不同模型不用为每个工具单独申请和轮换密钥。对写 Oracle 脚本这种需要反复生成、校验、对比的场景省下的就是切换成本。你需要准备三样东西我把它称为「三件套」缺一不可Base URLhttps://taotoken.net/apiAPI Key在控制台创建地址是https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewriteModel ID按你用的工具填对应模型标识比如做代码生成和校验时选一个擅长 SQL 的模型如果你用的是 Claude Code 这类编码工具接入时同样填这三件套Base URL 用上面的 API 地址Key 用控制台生成的Model ID 按工具要求填。想先验证模型能不能正常对话可以去模型对话页试一句https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite。长期做编码和 Agent 任务的话Coding Plan 更划算https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite。这里给一个通用的配置片段很多工具都认这种 JSON 结构路径按你实际工具的配置文件放{ base_url: https://taotoken.net/api, api_key: sk-你的TaoToken密钥, model: 你的模型ID }注意base_url只写到/api不要自己拼/v1/chat/completions之类的后缀工具通常会自己补全。Key 不要提交到 Git放环境变量或本地配置文件里。配好之后你就能让 AI 帮你生成下面的游标脚本再拿回 SQL*Plus 或 SQL Developer 里跑形成「生成—执行—纠错」的闭环。3. 可复制配置建表脚本与三类游标完整示例这一节是全文的技术核心所有脚本都可以直接复制执行。先建两张表一张模拟号段资源一张做隐式游标的更新测试。-- 建表号段资源表 create table gsm_resource ( gsmno varchar2(11), status varchar2(1), price number(8,2), store_id varchar2(32) ); insert into gsm_resource values(13905310001,0,200.00,SD.JN.01); insert into gsm_resource values(13905312002,0,800.00,SD.JN.02); insert into gsm_resource values(13905315005,1,500.00,SD.JN.01); insert into gsm_resource values(13905316006,0,900.00,SD.JN.03); commit; -- 建表隐式游标测试表 create table zrp (str varchar2(10)); insert into zrp values (ABCDEFG); insert into zrp values (ABCXEFG); insert into zrp values (ABCYEFG); insert into zrp values (ABCDEFG); insert into zrp values (ABCZEFG); commit;3.1 显式 cursor声明、打开、提取、关闭四步走显式游标有明确的cursor ... is select ...声明生命周期是 declare → open → fetch → close。它的作用域是当前 PL/SQL 块静态 SQL效率高但不能返回给客户端。declare cursor get_gsmno_cur (p_nettype in varchar2) is select gsmno from gsm_resource where gsmno like p_nettype || % and status 0; v_gsmno gsm_resource.gsmno%type; begin open get_gsmno_cur(139); loop fetch get_gsmno_cur into v_gsmno; exit when get_gsmno_cur%notfound; dbms_output.put_line(显式游标输出: || v_gsmno); end loop; close get_gsmno_cur; end; /注意这里我用了like p_nettype || %而不是原文的nettype p_nettype因为gsmno是完整号码用前缀匹配更贴近「按号段选号」的语义。执行后你会看到13905310001、13905312002、13905316006三条 status0 的记录被打印出来。%notfound在 fetch 之后判断这是显式游标循环的标准写法别用NO_DATA_FOUND。3.2 隐式 cursorDML 背后的 SQL 游标隐式游标没有声明Oracle 把每条 DML 都解析成一个名为SQL的隐式游标。SQL%ROWCOUNT、SQL%FOUND、SQL%NOTFOUND就是它的属性。FOR 循环遍历查询结果也是隐式游标。begin update zrp set str updateD where str like %D%; if sql%rowcount 0 then insert into zrp values (1111111); end if; dbms_output.put_line(第一次更新影响行数: || sql%rowcount); end; / begin update zrp set str updateD where str like %S%; if sql%rowcount 0 then insert into zrp values (0000000); end if; dbms_output.put_line(第二次更新影响行数: || sql%rowcount); end; /第一次更新匹配到两条含 D 的记录sql%rowcount 2不插入第二次匹配含 S 的记录为 0 条sql%rowcount 0于是插入0000000。这就是隐式游标最实用的地方用SQL%ROWCOUNT做「更新不到就插入」的 upsert 逻辑不用额外声明任何游标。FOR 循环版本更简洁连变量都不用声明begin for rec in (select gsmno, status from gsm_resource) loop dbms_output.put_line(rec.gsmno || -- || rec.status); end loop; end; /3.3 REF CURSOR 与 SYS_REFCURSOR把结果集交出去REF CURSOR 是动态游标运行时才绑定查询。它最大的价值是能返回给客户端这是存储过程返回结果集的唯一方式。SYS_REFCURSOR是 Oracle 9i 之后系统预定义的弱类型 REF CURSOR省去了自己type ... is ref cursor的声明。先看强类型 REF CURSOR它用return子句约束了结果集的列结构declare type gsm_rec is record( gsmno varchar2(11), status varchar2(1), price number(8,2)); type app_ref_cur_type is ref cursor return gsm_rec; my_cur app_ref_cur_type; my_rec gsm_rec; begin open my_cur for select gsmno, status, price from gsm_resource where store_id SD.JN.01; fetch my_cur into my_rec; while my_cur%found loop dbms_output.put_line(my_rec.gsmno || # || my_rec.status || # || my_rec.price); fetch my_cur into my_rec; end loop; close my_cur; end; /强类型的好处是编译期就能校验列匹配坏处是灵活性差。实际项目里更常用SYS_REFCURSOR做存储过程出参create or replace procedure getEmpByDept( in_deptNo in number, out_curEmp out sys_refcursor ) as begin open out_curEmp for select gsmno, status, price from gsm_resource where status 0; exception when others then raise_application_error(-20101, Error in getEmpByDept: || sqlcode); end getEmpByDept; /调用时用绑定变量接收var rset refcursor; exec getEmpByDept(10, :rset); print rset;print rset会把结果集直接打印出来这就是 REF CURSOR 能「跨边界」的证据——客户端拿到了结果集的读取权。对比一下显式游标无论怎么 open客户端都看不到它的数据。4. 验证请求执行输出与三类游标行为对比脚本跑完我们逐条看输出验证语义差异。显式游标那段输出三行139开头的可用号码证明它按参数化查询筛选、循环提取、正常关闭。隐式游标两段第一段sql%rowcount 2第二段sql%rowcount 0并插入0000000证明 DML 隐式游标属性可用。强类型 REF CURSOR 输出13905310001#0#200和13905315005#1#500正好是SD.JN.01门店的两条记录。SYS_REFCURSOR过程调用后print rset返回 status0 的三条记录。把差异整理成一张对照表方便你按场景选型维度显式 cursor隐式 cursorREF CURSOR / SYS_REFCURSOR声明方式cursor ... is select无声明DML 自动生成type ... is ref cursor或sys_refcursor绑定时机编译期静态 SQL编译期运行时动态绑定能否返回客户端不能不能能存储过程出参标准做法作用域当前 PL/SQL 块当前语句可跨子程序传递效率高高相对低仅在必要时用典型场景参数化遍历、批量处理upsert、FOR 循环返回结果集、子程序共享再补一组游标属性的区别这是排错时最容易混的%FOUND有行返回为 TRUE%NOTFOUND无行返回为 TRUE%ISOPEN游标仍打开为 TRUE%ROWCOUNT最近一条 SQL 影响的行数关键提醒SELECT ... INTO无数据触发NO_DATA_FOUND显式游标 where 未命中触发%NOTFOUNDUPDATE/DELETE未命中触发SQL%NOTFOUND。在 fetch 循环里判断退出条件用%NOTFOUND或%FOUND不要用NO_DATA_FOUND否则循环行为会不符合预期。如果你想让 AI 帮你核对某个脚本的游标类型选得对不对可以把脚本贴进模型对话页让它逐行指出「这里该用隐式还是 REF CURSOR」。用 TaoToken 统一 Key 的好处是你换模型对比结论时不用重新配环境。5. 本篇常见错排查从真实报错定位游标问题这一节按真实报错来每条都给出原因和修法。PLS-00382: expression is of wrong type。最常见于把静态 cursor 当出参返回。存储过程出参必须是 REF CURSOR 类型不能是cursor ... is select声明的静态游标。修法把出参改成out sys_refcursor过程内用open out_cur for select ...。ORA-01001: invalid cursor。通常是游标没 open 就 fetch或者 close 之后又 fetch。显式游标必须严格 open → fetch → closeREF CURSOR 在open ... for之前不能 fetch。检查你的%ISOPEN判断。ORA-06550 / PLS-00201: identifier SYS_REFCURSOR must be declared。多见于老版本客户端或权限问题。SYS_REFCURSOR是 9i 之后系统预定义的确认数据库版本并检查当前 schema 是否有权限。实在不行就自己声明type rc is ref cursor;替代。local proxy failed / 401。如果你在 AI 工具里生成脚本时报这类错多半是 Base URL 或 Key 配错了。检查三件套Base URL 是否为https://taotoken.net/api、Key 是否从控制台正确复制、Model ID 是否填对。401 一般是 Key 无效或过期去https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite重新生成。local proxy failed通常是工具的网络配置问题确认 Base URL 没有多余后缀。reading choices 报错 / 返回结构解析失败。这通常是模型返回格式和工具预期不一致换一个 Model ID 或检查工具版本。接入文档里有各工具的配置说明https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite。OAuth 相关报错。如果你用的是 Claude Code 这类带 OAuth 流程的工具接入自定义 Base URL 时可能提示 OAuth 失败。这类工具通常支持 API Key 模式切到 Key 模式填三件套即可参考 Claude Code 接入说明https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaude_codeutm_campaignrewrite。强类型 REF CURSOR 列不匹配。type ... is ref cursor return gsm_rec要求open ... for的 select 列顺序、类型和gsm_rec完全一致多一列少一列都编译不过。修法要么严格对齐要么改用sys_refcursor弱类型。%ROWCOUNT 取值不符合预期。记住SQL%ROWCOUNT统计的是最近一条 DML 影响的行数不是整个块。在 fetch 循环里cursor%ROWCOUNT是已提取的行数两者别混。6. 把游标脚本接进你的 AI 工作流三类游标的边界其实很清晰显式游标管块内参数化遍历隐式游标管 DML 和 FOR 循环REF CURSOR / SYS_REFCURSOR 管跨边界返回结果集。选型优先级就是「隐式 显式 REF CURSOR」只有结果集必须交给客户端或在子程序间共享时才升级到 REF CURSOR。实际写脚本时我建议你把建表脚本和游标示例一起丢给 AI让它按「声明—打开—提取—关闭」逐段检查再拿回数据库执行验证。用 TaoToken 统一 Key 后生成、校验、换模型对比都在一个通道里完成不用反复配环境。需要长期做这类编码和 Agent 任务的Coding Plan 比按次调用更省心https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite。先把上面三段脚本跑通再对照报错清单排查你对这三类游标的判断就会从「背概念」变成「看场景」。
返回列表