
一段 PL/SQL 匿名块里声明了 ref cursor 类型和游标变量按 v_job 是否为 saler 打开不同结果集最后 while cur%found 提取dbms_output 却一条都不输出。要让 Codex 帮你定位这个游标变量问题得先有模型通道到 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 创建 YOUR_API_KEY再改 Codex 配置。这个现象通常不是 open 分支没跑而是 %found 在第一次 fetch 之前就被拿来判断了。TaoToken 在这里只负责把 Codex 接到可用模型上不执行 PL/SQL真正跑匿名块的是你本地的 SQL*Plus 或 SQL Developer。把顺序调对再让 Codex 只做代码解释排障会快很多。1. dbms_output 一直空着先别怀疑 open 分支1.1 原始 declare 块的执行顺序原文那段代码的结构是先声明type mycur is ref cursor再把cur实例化成游标变量接着select job into v_job用if v_job saler决定open cur for哪一个结果集最后用while cur%found包住dbms_output.put_line和fetch。很多人看到没有输出第一反应是open没执行或者表里没数据。把执行顺序拆开看open cur for ...确实会给游标绑定一个结果集但绑定不等于已经取出第一行。游标此时只是“准备好了”行还在结果集里等着fetch去取。%FOUND这个属性描述的是“上一次 fetch 有没有取到行”而不是“结果集里有没有行”。第一次fetch之前上一次 fetch 根本不存在所以属性没有真值。dbms_output空着往往不是数据问题而是循环条件在第一次判断时就为假。尤其当异常处理里只写了dbms_output.put_line(sqlcode||sqlerrm)如果select into或open阶段有问题真正的报错也可能被这段兜底逻辑吞掉看起来就像“什么都没发生”。1.2 %FOUND 在第一次 fetch 前到底是什么在 PL/SQL 里显式游标和游标变量都有%FOUND、%NOTFOUND、%ROWCOUNT、%ISOPEN这几个属性。open之后、第一次fetch之前%FOUND的值是 NULL%NOTFOUND也是 NULL%ROWCOUNT为 0。while cur%found等价于判断一个 NULL 条件PL/SQL 会把 NULL 当成 FALSE 处理所以循环直接跳过。这就是为什么open分支明明进入了dbms_output也没有内容。循环体一次都没执行fetch自然也没机会运行。原代码里即使循环能进入dbms_output.put_line(v_name)还写在fetch前面第一行输出时v_name尚未赋值也会先打印一个空值。两个顺序问题叠在一起表现就是“游标变量没有输出”。正确的判断节奏应该是先fetch一次让游标真正取到第一行再根据%FOUND决定是否进入循环循环体里处理当前行处理完再fetch下一行。或者使用loop加exit when cur%notfound把退出判断放在fetch之后。1.3 先把 Codex 的模型通道接上再让它读这段代码Codex 很适合做这种“对照执行顺序”的活你把完整的declare块贴进去让它指出哪一行先执行、哪一行后执行它不碰你的数据库只生成解释和修改建议。但前提是 Codex 能稳定调用模型。官方通道额度、模型切换、多 Key 管理这些事容易打断排障节奏所以这里用 TaoToken 作为 Codex 的模型通道。先把通道配好再进入游标本身。配置只改~/.codex/config.tomlBase URL 填https://taotoken.net/apiKey 用环境变量注入。下面先拿 Key再写配置最后回到 PL/SQL。2. 在 ~/.codex/config.toml 里把 Codex 指到 TaoToken 的 API 通道2.1 从官网拿 YOUR_API_KEY打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 注册或登录后进入控制台创建一个 API Key。创建时建议单独给 Codex 建一把不要和别的工具混用后面要对账也清楚。复制出来的 Key 不要写进文章、截图或 Git放到环境变量里代码和配置里统一用YOUR_API_KEY占位。如果你已经有 Key直接跳到下一步。Key 的权限、可用模型、额度这些信息都在控制台里看模型 ID 也以模型广场当时列表为准不要凭记忆写一个不存在的名字。配置 Codex 只需要两样东西一把有效的 Key一个当前可用的模型 ID。2.2 model_provider 和 base_url 的写法Codex 的配置文件在~/.codex/config.toml。下面这段把模型供应商指向 TaoTokenBase URL 是https://taotoken.net/api末尾不要加/v1也不要在这里加任何 UTM 参数。model先写YOUR_MODEL_ID具体值去模型广场看不要编造日期后缀或不存在的大版本号。model YOUR_MODEL_ID model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY wire_api chat然后在终端里把 Key 放进环境变量。macOS、Linux 或 Git Bashexport TAOTOKEN_API_KEYYOUR_API_KEYWindows PowerShell$env:TAOTOKEN_API_KEYYOUR_API_KEY注意这里是 Codex不要套ANTHROPIC_*变量。ANTHROPIC_BASE_URL、ANTHROPIC_AUTH_TOKEN是 Claude Code 那一套配错文件会出现“改了但没生效”的错觉。Codex 读的是model_provider和model_providers里的base_url。2.3 验证 Codex 能不能回答 PL/SQL 问题保存config.toml后新开一个终端运行codex先问一个不碰数据库的小问题例如“显式游标在 open 之后、第一次 fetch 之前%FOUND 的值是什么”。如果 Codex 能正常回答说明 Key、Base URL 和模型 ID 这条链路通了。如果报 401先看环境变量有没有在当前终端生效如果报 404先检查 Base URL 是不是被写成了https://taotoken.net/api/v1。模型 ID 不对时错误信息通常会直接提示模型不存在回模型广场复制当前可用 ID。这一步只验证“Codex 能调用模型”不要让它连数据库也不要让它执行 SQL。你只是拿它当代码解释器真正的 PL/SQL 匿名块在本地 SQL*Plus 或 SQL Developer 里跑。3. 把原始 declare 整段贴给 Codex只让它改游标循环3.1 贴代码的边界从 declare 到 end;很多人只把while cur%found loop ... end loop;这几行贴给 Codex结果模型只能猜上下文。要定位游标变量为什么没有输出必须把declare到end;整段贴进去包括type mycur is ref cursor、cur mycur、select job into v_job、两个open cur for分支以及最后的exception。这样 Codex 才能看到open已经执行但%FOUND在第一次fetch之前被判断dbms_output.put_line又写在fetch之前。只贴循环部分模型可能会建议你改where条件或检查表数据反而绕远路。3.2 提问提示词把代码贴进去后提示词可以这样写“下面是一段 PL/SQL 匿名块执行后 dbms_output 没有任何输出。请只检查游标变量cur的fetch与%FOUND顺序指出为什么while循环不进入并给出最小修改。不要改where条件和业务逻辑。你不能连接数据库只生成和解释 SQL我会在本地 SQL*Plus 执行把输出或报错贴回来。”这段话把边界说清楚了Codex 只生成、解释、对照代码不执行。你不给它数据库连接也不让它跑诊断 SQL。它给出的修改建议你复制到本地编辑器再手动执行。3.3 Codex 该指出的两个顺序问题第一while cur%found出现在第一次fetch之前。open只负责绑定结果集不负责取行%FOUND描述的是上一次fetch的结果第一次判断时没有“上一次”所以是 NULL循环不进入。第二循环体里先dbms_output.put_line(v_name)后fetch cur into v_name。就算循环能进入第一轮输出时v_name还没有被赋值打印出来也是空。正确的顺序是fetch在前put_line在后或者把fetch放到循环条件之前。这两个问题不涉及业务 SQL只改循环结构。Codex 如果建议你加dbms_output.put_line(v_job||v_job)做调试可以保留它不会改变结果集只是帮你确认if走了哪个分支。4. 修正后的 ref cursor 版本先 fetch 再判断 %found4.1 while cur%found 版本下面这个版本保留while cur%found的写法但把第一次fetch提到循环之前。每次循环体处理当前行然后再取下一行。close cur放在异常块外异常块里用%ISOPEN判断避免游标没打开就关闭。declare type mycur is ref cursor; cur mycur; v_name emp.ename%type; v_job emp.job%type; begin select job into v_job from emp where empno 111; if v_job saler then open cur for select ename from emp where sal 3000; else open cur for select ename from emp where sal 3000; end if; fetch cur into v_name; while cur%found loop dbms_output.put_line(v_name); fetch cur into v_name; end loop; close cur; exception when others then if cur%isopen then close cur; end if; dbms_output.put_line(sqlcode || sqlerrm); end; /注意where empno 111是否合适取决于empno列类型。如果empno是数字列最好写where empno 111避免隐式类型转换。如果原表就是字符型保留引号。这个细节不要交给 Codex 猜它看不到你的表结构你可以在提示词里说明列类型。4.2 loop exit when cur%notfound 版本另一种更常见的写法是loop配exit when cur%notfound。把fetch放在循环体开头取完立刻判断是否还有行有就处理没有就退出。这种结构不容易漏掉第一次fetch。declare type mycur is ref cursor; cur mycur; v_name emp.ename%type; v_job emp.job%type; begin select job into v_job from emp where empno 111; if v_job saler then open cur for select ename from emp where sal 3000; else open cur for select ename from emp where sal 3000; end if; loop fetch cur into v_name; exit when cur%notfound; dbms_output.put_line(v_name); end loop; close cur; exception when others then if cur%isopen then close cur; end if; dbms_output.put_line(sqlcode || sqlerrm); end; /两个版本没有绝对优劣区别在退出判断的位置。排障时先选一种在本地跑通再把输出贴回 Codex让它对照原代码说明改了哪几行。不要一次改太多否则出问题时不知道是哪个改动引起的。4.3 在本地 SQL*Plus 执行并看输出在 SQL*Plus 里先执行set serveroutput on再粘贴匿名块最后用/运行。如果用的是 SQL Developer打开 DBMS Output 面板并启用输出。执行后应该能看到ename列表如果仍然没有输出不要急着让 Codex 继续改 SQL先把以下信息收集齐v_job的实际值、select job into v_job是否抛异常、sal列的数据分布、serveroutput是否开启。把这些结果贴回 Codex 的对话里让它基于真实现象判断。Codex 只能看到你贴的代码和输出它看不到数据库里的表也不会替你执行fetch。排障链是Codex 生成或解释 SQL你在本地执行再把报错或输出贴回对话。5. 改了顺序仍没输出v_job、serveroutput 和 open 分支5.1 v_job 的值与大小写if v_job saler依赖v_job的实际值。如果表里存的是SALER、Saler或者前后有空格条件会走else分支。更麻烦的是select job into v_job如果查不到empno对应的行会抛NO_DATA_FOUND然后跳到exception被when others捕获只打印错误码和错误信息。此时open cur可能根本没执行。临时加一行dbms_output.put_line(v_job[ || v_job || ]);放在if之前执行后看方括号里的值。如果是空、大小写不对或带空格先修数据或修判断条件。如果这行都没有输出说明select into阶段就出问题了优先检查empno的值和列类型。5.2 set serveroutput on这是最容易被忽略的一步。dbms_output.put_line只是把内容写进缓冲区客户端不一定会显示。SQL*Plus 里不执行set serveroutput on匿名块执行成功也可能看不到任何输出。SQL Developer 要在“DBMS Output”窗口里点加号并启用。如果你把匿名块贴给 Codex 时没提客户端它可能会默认输出已经开启。排障时把“我在 SQL*Plus已经 set serveroutput on”写进提示词模型就不会把方向带到“是否需要开启输出”上。5.3 sal 边界与表数据sal 3000和sal 3000会漏掉sal 3000的行。如果v_job不是saler走else分支结果集里只有sal 3000的记录如果表里恰好没有这个区间的数据游标打开了也没有行fetch第一次就%NOTFOUND循环不执行这是正常结果。先单独跑select ename from emp where sal 3000;确认有没有数据再判断循环逻辑。还有一种情况empno查不到行导致select into抛异常直接进入exception你看到的输出只有sqlcode||sqlerrm。所以不要只看“有没有 ename 输出”也要看异常分支有没有打印。5.4 游标没 open 就 close 的异常如果select into或if分支里出了异常程序跳到exception此时cur可能还没open。原代码如果直接close cur会再抛ORA-01001: invalid cursor。加上if cur%isopen then close cur; end if;可以让异常处理更稳。这个判断不影响正常路径只在出错时避免二次异常。改完这一处后重新在本地执行再把新的输出或报错贴回 Codex。让它对照你贴回的结果确认是数据问题、大小写问题还是游标属性顺序问题。不要让它直接连 Oracle也不要让它生成impdp、FETCH跨库脚本那不属于这段排障的范围。6. 这套排障跑通后去控制台对一下 Codex 调用游标修好之后回到 Codex 侧做一次收尾。先在 TaoToken 模型对话 里用同一把 Key 发一条测试消息确认模型 ID 和 Base URL 没串如果你打算长期用 Codex 写 PL/SQL、改存储过程可以打开 Coding Plan 看套餐是否够用。Key 统一在 控制台 API Keys 创建模型广场和用量也在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 里看。Claude Code 的环境变量对照在 接入文档但本篇走的是 Codex别把config.toml和 Claude Code 的settings.json混在一起。最后提醒一句%FOUND第一次判断前必须先fetch这是游标变量的属性决定的不是模型能“猜”出来的。Codex 的价值在于帮你对照顺序、生成最小修改真正的执行和验证仍然在本地 SQL*Plus 里完成。把输出贴回对话再让它解释改法排障链路就闭环了。