
1. 游标里按 EMPNO 给 SAL 赋值为什么容易改错1.1 原逻辑拆开看这段 PL/SQL 我最早是在改 scott.emp 的薪资赋值逻辑时遇到的MY_CUR 游标带着 FOR UPDATE OF SAL 锁住行然后按 EMPNO 的 MOD 10/11 走不同分支再用 DEPTNO 前两位做除数、TRUNC 后写回 SAL。表面看只有两个条件实际改起来要同时盯住四件事游标锁、分支顺序、除数为 0、END IF 配对。老实说分支一多END IF 和 NULLIF 很容易对不齐。后来我用 Codex 来改写Codex 的模型通道走的是 TaoToken也就是先到 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 创建 API Key再把 Base URL 填进 Codex 配置。TaoToken 只负责把模型请求送出去真正对 SAL 的赋值逻辑仍然由 Codex 生成代码、由你在 SQL*Plus 里执行。先看原始需求scott.emp 表里EMPNO 大于 7698 的员工要根据 EMPNO 的不同性质把 DEPTNO 前两位拿来做除数最后把结果写回 SAL。代码用游标 MY_CUR 加 FOR UPDATE OF SAL 锁定目标行保证在 UPDATE 之前没有其他会话改这一列的 SAL 值。这个锁不是表锁是行级锁锁的范围就是游标当前扫到的行。原代码的关键结构整理后是这样FOR EMP_ROW IN MY_CUR LOOP NUM : TRUNC( TO_NUMBER(EMP_ROW.EMPNO) / NULLIF(TO_NUMBER(SUBSTR(EMP_ROW.DEPTNO, 1, 2)), 0) ); IF MOD(EMP_ROW.EMPNO, 10) 0 THEN UPDATE scott.emp E SET E.SAL TRUNC(TO_NUMBER(EMP_ROW.EMPNO) / NUM) WHERE CURRENT OF MY_CUR; ELSE IF MOD(EMP_ROW.EMPNO, 11) 0 THEN UPDATE scott.emp E SET E.SAL TRUNC(TO_NUMBER(EMP_ROW.EMPNO) / NUM) WHERE CURRENT OF MY_CUR; END IF; END IF; END LOOP;WHERE CURRENT OF MY_CUR的含义是当前 UPDATE 只作用于游标刚取出来的这一行不需要再写 WHERE EMPNO 某某。它依赖FOR UPDATE OF SAL建立的游标行锁两者是配套出现的。1.2 分支一多END IF 和 NULLIF 对不齐原文最大的坑不是计算本身而是结构ELSE IF嵌套后有两个END IF少写一个Oracle 只报 PLS-00103不会告诉你「这里少了一个 END IF」。另一个坑是除数为 0如果 DEPTNO 前两位是 00SUBSTR结果是 0NULLIF把它变成 NULLNUM 就成了 NULL继续拿 NUM 去除 EMPNO结果还是 NULL最后 SAL 会被写成 NULL。原代码只在第一层除法用了NULLIF第二层TRUNC(EMPNO / NUM)没有保护。这些坑在代码短的时候看不出来一旦分支从两个变成五个人工核对 END IF 的体力活就容易出错。这也是后来我把这段丢给 Codex 改写的原因。1.3 还有两个边界Codex 改的时候也得注意第一MOD(EMP_ROW.EMPNO, 10) 0和MOD(EMP_ROW.EMPNO, 11) 0可能同时成立比如 EMPNO 7700 时能同时被 10 和 11 整除。原代码先判断 10所以走第一个分支改成 ELSIF 后也仍然是第一个分支优先。如果 Codex 把两个 IF 写成并列后面的分支会覆盖前面的结果SAL 的赋值行为就变了。第二DEPTNO 是 NUMBER 类型SUBSTR(EMP_ROW.DEPTNO, 1, 2)依赖 Oracle 的隐式类型转换。练习表上问题不大但改到生产环境最好先TO_CHAR(EMP_ROW.DEPTNO)再截取前两位。这两条都可以写进给 Codex 的提示词里避免它自由发挥。2. 用 Codex 改写之前先到 TaoToken 拿 Key 和 Base URL2.1 Codex 只负责生成代码不碰 SAL 字段先澄清一点Codex 不是直接连上 Oracle 去 UPDATE 数据的。它的任务是读懂这段 PL/SQL把分支结构改清晰、补全 END IF、加好 NULLIF 保护然后生成一段你可以直接贴进 SQLPlus 执行的脚本。TaoToken 在这里只充当 Codex 的模型通道既不接触 scott.emp也不碰 SAL 字段本身。真正对 SAL 的赋值动作发生在你本地 SQLPlus 会话里。所以步骤变成先配通 Codex再让 Codex 改写最后你本机执行。配置 Codex 的第一步是到 TaoToken 注册并创建 API Key。2.2 Codex 配置文件里填入 TaoToken 的 Base URL创建 Key 的入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 控制台的 API Keys 页面。拿到 Key 后编辑本机的~/.codex/config.tomlmodel YOUR_MODEL_ID model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key OPENAI_API_KEY wire_api chat注意两个地址不是一回事官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 是注册、创建 Key、看模型广场、看用量的地方~/.codex/config.toml里的base_url必须填https://taotoken.net/api末尾不要加/v1。模型 ID 以 TaoToken 模型广场当时列出来的为准把YOUR_MODEL_ID替换成真实 ID。保存 config.toml 后在终端里把 Key 导出成环境变量export OPENAI_API_KEYYOUR_API_KEYYOUR_API_KEY换成控制台创建出来的那串 Key。然后跑一句codex exec 11之类的测试能正常返回就说明通道通了。3. 让 Codex 改写游标赋值分支3.1 给 Codex 的提示词怎么写把原代码连同需求一起贴给 Codex重点说明游标是 FOR UPDATE OF SALEMPNO 被 10 整除走一个分支被 11 整除走另一个分支其他情况 SAL 置 100除数来自 DEPTNO 前两位用 NULLIF 防止除数为 0最后把结果 TRUNC 后写回。同时要求把嵌套的 ELSE IF 改成 ELSIF减少 END IF 层级。可以直接用下面这段提示词下面这段 Oracle PL/SQL 用游标 MY_CUR 扫描 scott.emp 中 EMPNO 7698 的行 对满足 MOD(EMPNO,10)0 或 MOD(EMPNO,11)0 的行用 DEPTNO 前两位作为除数 计算 TRUNC(EMPNO / 除数) 并更新 SAL其余行 SAL 置 100。 请改写为更稳的版本 1. 保留 FOR UPDATE OF SAL 和 WHERE CURRENT OF MY_CUR 2. 用 ELSIF 替代嵌套 ELSE IF补全 END IF 3. 对每一处除法都用 NULLIF 防止 ORA-01476 4. 保持 MOD(EMPNO,10)0 优先于 MOD(EMPNO,11)0 5. 输出完整 PL/SQL 块不要解释。3.2 改写后的大致样子Codex 给出的结果不会只有一种但核心结构会长成这样DECLARE CURSOR MY_CUR IS SELECT E.EMPNO, E.DEPTNO, E.SAL FROM scott.emp E WHERE E.EMPNO 7698 FOR UPDATE OF SAL; V_EMPNO NUMBER; V_DIV NUMBER; V_NUM NUMBER; BEGIN FOR EMP_ROW IN MY_CUR LOOP V_EMPNO : TO_NUMBER(EMP_ROW.EMPNO); V_DIV : TO_NUMBER(SUBSTR(TO_CHAR(EMP_ROW.DEPTNO), 1, 2)); V_NUM : TRUNC(V_EMPNO / NULLIF(V_DIV, 0)); IF MOD(EMP_ROW.EMPNO, 10) 0 THEN UPDATE scott.emp E SET E.SAL TRUNC(V_EMPNO / NULLIF(V_NUM, 0)) WHERE CURRENT OF MY_CUR; ELSIF MOD(EMP_ROW.EMPNO, 11) 0 THEN UPDATE scott.emp E SET E.SAL TRUNC(V_EMPNO / NULLIF(V_NUM, 0)) WHERE CURRENT OF MY_CUR; ELSE UPDATE scott.emp E SET E.SAL 100 WHERE CURRENT OF MY_CUR; END IF; END LOOP; COMMIT; END; /这段相对原代码的改动是把ELSE IF变成了ELSIF整个块只剩一个END IFDEPTNO先TO_CHAR再截取避免隐式转换两处除法都套了NULLIF。如果 Codex 坚持用嵌套ELSE IF在提示词里追加一句「不要嵌套全部改成 ELSIF」即可。3.3 Codex 可能擅自改掉游标语义要拦一下Codex 这类模型有个特点看到「按条件更新 SAL」容易顺手优化成一条MERGE或者多条UPDATE合并然后告诉你「这样更高效」。但在这个场景里不能接受因为FOR UPDATE OF SAL和WHERE CURRENT OF MY_CUR是原文指定的行锁语义合并成集合 UPDATE 后锁的粒度、扫描顺序都可能变。所以提示词里要明确写「保留游标和 WHERE CURRENT OF MY_CUR」。如果改写结果里没有游标就让它重写不要将就。4. 在 SQL*Plus 里验证改写结果4.1 先备份再执行Codex 生成的脚本不要直接对业务表跑。scott.emp 是练习表但习惯要养好执行前先建一张备份表CREATE TABLE emp_bak_202409 AS SELECT * FROM scott.emp;然后在 SQL*Plus 里执行整个 PL/SQL 块。执行完用下面的对照语句验证SELECT EMPNO, DEPTNO, SAL FROM scott.emp WHERE EMPNO 7698 ORDER BY EMPNO;重点看两类行EMPNO 以 0 结尾的SAL 应该是 EMPNO 除以 NUM 再 TRUNC 的结果EMPNO 是 11 的倍数的走第二个分支其他行 SAL 是 100。如果和预期不符把结果贴回 Codex让它对照调整。4.2 报错对照ORA-01476 和 PLS-00103这段改写最容易踩的报错有两个。第一个是ORA-01476: divisor is equal to zero说明某一行算出来的除数是 0NULLIF没起作用或没套对位置。检查NULLIF是否同时出现在两处除法上。第二个是PLS-00103通常伴随END IF缺失说明 Codex 这次生成的块又嵌套回去了。解决办法是让 Codex 只输出一个平面的 IF-ELSIF-END IF不要保留 ELSE IF 嵌套。如果你在 Codex 配置阶段就报了 404先回 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 的模型广场确认YOUR_MODEL_ID是不是当前列表里的 ID如果是 401检查OPENAI_API_KEY是否导出正确或者 Key 是否在控制台被删了。4.3 先打印再更新确认中间值如果你不想第一次就跑 UPDATE可以让 Codex 生成一个调试版本只计算不更新用DBMS_OUTPUT.PUT_LINE把 EMPNO、DEPTNO、V_DIV、V_NUM 和新 SAL 都打出来。在 SQL*Plus 里先执行SET SERVEROUTPUT ON再跑调试块确认每行的计算结果都符合预期后再执行真正的 UPDATE 版本。这种「先生成、后验证、再写回」的流程正好发挥 Codex 改代码的用途也不会让它在你的库里乱动数据。5. 跑通之后去控制台对一下这次调用5.1 在模型对话里试同一把 Key配置保存后先在 TaoToken 模型对话 里用同一把 Key 发一条测试消息确认模型 ID 和 Base URL 没填错。如果对话正常Codex 的请求也会正常因为两者走的是同一个 API 通道只是前端工具不同。5.2 Coding Plan 与 Key 管理如果你打算让 Codex 长期承担这类 Oracle 改写工作可以打开 Coding Plan 看看套餐是否够用。Key 的创建和吊销都在 TaoToken 控制台 API Keys 页面建议每次只开一把 Key配一个工具出问题好定位。这次 Codex 调用有没有正常记账也能在官网用量页面确认。