oracle 中 ‘‘ 与 null 的 update 陷阱,让 Codex 走 TaoToken 排查
一段看起来毫无问题的 PL/SQL游标在跑异常也接了update 却像被空气挡住。问题出在v_new : ;这一行。在 Oracle 里不是空字符串它就是null。null参与比较时结果既不是 true 也不是 false而是 unknown所以if v_new then后面的 update 永远进不去。这类 oracle 中与null的 update 陷阱我打算用 Codex 走 TaoToken 的统一通道来快速定位先去 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 注册并创建 API Key把 Codex 的 Base URL 指向 https://taotoken.net/api然后把下面这段 PL/SQL 原样贴给模型让它解释为什么 update 被跳过。TaoToken 在这里只做模型通道不碰你的数据库诊断 SQL 由你在本地执行报错再贴回对话。1. 那段 PL/SQL 的 update 为什么被跳过1.1 先看问题代码的执行路径把原始逻辑抽象成三段游标扫 dno子查询查映射条件判断后 update。问题不在游标也不在 update 语法而在异常分支和判断条件。下面这段是问题版本结构一样变量名做了替换declare v_new_dno varchar2(20) : ; cursor cur_dno is select distinct dno from t_old where dno is not null; r_dno cur_dno%rowtype; begin open cur_dno; loop fetch cur_dno into r_dno; exit when cur_dno%notfound; begin select new_dno into v_new_dno from t_map where old_dno r_dno.dno; exception when no_data_found then v_new_dno : ; end; if v_new_dno then update t_old set dno v_new_dno where dno r_dno.dno; end if; end loop; close cur_dno; end; /跑的时候游标会正常打开fetch 也会拿到数据。子查询命中的时候v_new_dno得到一个非空值子查询没命中的时候异常分支把v_new_dno赋成。然后if v_new_dno 开始判断。你以为是空字符串但 Oracle 把它当null。null和任何值做比较结果都是 unknownif只认 trueunknown 等于 false于是 update 被跳过。1.2 Oracle 中就是null的官方语义Oracle 的文档里写得很清楚Oracle 数据库当前把长度为零的字符串当作null处理。也就是说char或varchar2类型的空字符串在存储和比较时都等同于null。你写v_new : 实际执行的是v_new : null。这一点和很多其他数据库不一样MySQL 里和null是分开的所以习惯 MySQL 的人第一次写 Oracle PL/SQL 很容易中招。这个等价关系还会影响很多地方比如length()返回null而不是 0 is null返回 truenvl(, x)返回x。所以在 Oracle 里判断一个varchar2变量是不是“空”不能写 或 只能写is null或is not null。1.3 当碰到null三值逻辑的坑SQL 的比较运算不是二值逻辑而是三值逻辑true、false、unknown。null参与的任何比较除了is null和is not null结果都是 unknown。比如select case when null then true else false end from dual;这个语句会返回 false因为null 的结果是 unknownwhen只匹配 trueunknown 落到 else。同理if v_new_dno then在v_new_dno为null时永远不成立。即使v_new_dno不是null如果它是空字符串Oracle 也当null同样不成立。所以这个判断把“有值”和“没值”两种情况的 update 都挡掉了。2. 用 Codex 走 TaoToken 把这段 PL/SQL 拆开看2.1 准备打开官网创建 Key要复现这个排查过程先打开 TaoToken 注册并创建 API Key。Key 用占位符YOUR_API_KEY表示实际创建后复制出来。同时看一眼模型广场确认你要在 Codex 里用的模型 ID。模型 ID 以模型广场当时列表为准不要凭记忆写。这个统一通道提供 API 接入Codex 通过它把对话请求发给模型不需要你分别对接不同厂商。这里再给一次完整地址https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 注册、创建 Key、看模型列表都在这个页面进入。2.2 Codex 的 ~/.codex/config.toml 指向 TaoTokenCodex 的配置在用户目录下的~/.codex/config.toml。如果你还没建这个文件先创建。下面是一份可复制的配置注意 Base URL 写https://taotoken.net/api末尾不要加/v1model 你的模型ID以模型广场当时列表为准 model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY然后在终端里设置环境变量export TAOTOKEN_API_KEYYOUR_API_KEY如果你用的是 Windows PowerShell可以写$env:TAOTOKEN_API_KEYYOUR_API_KEY保存配置后启动 Codex。它会把请求发到https://taotoken.net/api由 TaoToken 转发到你选的模型。你的 Key 从 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 创建不要把它写进代码仓库。2.3 给 Codex 的提问模板只贴代码不贴连接串配置好之后把问题版本 PL/SQL 贴给 Codex。提问可以这样写下面这段 Oracle PL/SQL 的 update 一直不执行游标和异常都能走到。请帮我分析为什么 if 条件不成立并给出修改方案。不要连接数据库只解释代码和 Oracle 语义。 declare v_new_dno varchar2(20) : ; cursor cur_dno is select distinct dno from t_old where dno is not null; r_dno cur_dno%rowtype; begin open cur_dno; loop fetch cur_dno into r_dno; exit when cur_dno%notfound; begin select new_dno into v_new_dno from t_map where old_dno r_dno.dno; exception when no_data_found then v_new_dno : ; end; if v_new_dno then update t_old set dno v_new_dno where dno r_dno.dno; end if; end loop; close cur_dno; end; /提示不要把数据库连接串、生产库账号密码贴进对话。Codex 只做代码解释执行由你在本地 SQL*Plus 完成。关键点模型会根据 Oracle 的三值逻辑指出与null的等价关系。3. 模型排查 null 比较时的关键输出3.1 它会先指出异常分支把 v_new 赋成了 null模型看到v_new_dno : ;和exception里的v_new_dno : ;会立刻标出这两处。它会解释在 Oracle 里等价于null所以变量在初始化时就是null异常分支赋值后还是null。然后看if v_new_dno then条件里的也是null整个表达式变成null null结果 unknown。update 自然进不去。3.2 它会解释 if 判断的三值逻辑模型通常会用 dual 表举例select case when null null then true else false end as cmp_null_null, case when abc null then true else false end as cmp_value_null, case when is null then true else false end as empty_is_null from dual;结果会显示null null是 falseabc null也是 false is null是 true。所以判断varchar2是否有值应该用is not null而不是 。3.3 它会给出 is not null 的替代方案模型会建议把两处改成null把 改成is not null。它可能还会提醒如果业务上确实要区分“查不到”和“查到空字符串”在 Oracle 里也区分不了因为空字符串就是null。所以要么在子查询里用nvl给默认值要么在应用层处理。但就这个 update 场景最简单的修正就是is not null。4. 修正后的 PL/SQL 与本地验证4.1 两处改动v_new : null 和 is not null修正版本只需要改两个地方变量初始化赋值和异常分支赋值改成null判断条件改成is not null。declare v_new_dno varchar2(20) : null; cursor cur_dno is select distinct dno from t_old where dno is not null; r_dno cur_dno%rowtype; begin open cur_dno; loop fetch cur_dno into r_dno; exit when cur_dno%notfound; begin select new_dno into v_new_dno from t_map where old_dno r_dno.dno; exception when no_data_found then v_new_dno : null; end; if v_new_dno is not null then update t_old set dno v_new_dno where dno r_dno.dno; end if; end loop; close cur_dno; end; /4.2 在 SQL*Plus 里怎么确认 update 真的执行把修正后的代码放到本地 SQL*Plus 或你常用的 Oracle 客户端里执行。可以先在测试表上跑观察 update 影响的行数。也可以加一句dbms_output.put_line输出每次判断的v_new_dno值看看是null还是具体值。注意Codex 不会替你连数据库这些执行动作必须由你在本地完成。执行完把结果或报错贴回对话让模型继续帮你核对。4.3 为什么别用 nvl(v_new,) 继续绕有人会想既然就是null那我用nvl(v_new_dno, )把它转一下行不行答案是没用因为nvl的第二个参数还是null结果还是null。如果想用nvl给默认值得给一个真正非空的字符串比如nvl(v_new_dno, N/A)但这样 update 会把 dno 改成N/A通常不是业务想要的。所以最干净的做法还是is not null。5. Codex 配置与 Oracle 语义的排障清单5.1 Base URL 写成 https://taotoken.net/api不要加 /v1Codex 的config.toml里base_url填https://taotoken.net/api。有些人看到 OpenAI 的地址习惯性加/v1但在 TaoToken 这里末尾不加/v1否则可能 404。官网落地页是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 只用来注册、创建 Key、看模型广场和用量不要把它填到base_url里。5.2 401 与 404 分别代表什么如果 Codex 启动后报 401通常是 API Key 没设置对检查环境变量TAOTOKEN_API_KEY是否等于YOUR_API_KEY对应的真实值。如果报 404先看base_url是不是多写了/v1或者模型 ID 在模型广场里不存在。模型 ID 以 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 模型广场当时列表为准不要自己编。5.3 模型 ID 写错时 Codex 的报错模型 ID 写错时Codex 可能返回 model not found 或类似的错误。解决方式回到 TaoToken 模型广场页面复制准确的模型 ID覆盖config.toml里的model字段。不要把gpt-5或带日期后缀的猜测 ID 写进去除非模型广场里明确有。6. 跑完这次诊断去控制台对一下用量6.1 用同一把 Key 在模型对话里发一条测试消息配置保存后先在 TaoToken 模型对话 里用同一把 Key 发一条测试消息确认模型 ID 和 Base URL 没填错。比如直接问“Oracle 中和null有什么区别”看返回是否正常。模型对话能跑通Codex 的配置基本就没问题。6.2 创建 Key 与 Coding Plan 的入口如果你还没创建 Key去 控制台 API Keys 创建。长期用 Codex 写代码可以打开 Coding Plan 看套餐是否够用。这些页面都在同一个账号下Key 创建后可以继续用在 Codex 里。6.3 把这次排查记成自己的检查项这次问题的根因是 Oracle 的与null等价以及null不能参与比较。以后写 PL/SQL 条件判断时看到 或 就要警觉改成is not null或is null。如果还想让模型帮你复查其他 SQL可以把代码贴给 Codex但记住让它只解释执行留给本地 SQL*Plus。7. 下一步把这类陷阱变成固定检查动作7.1 空字符串与 null 的检查项在 Oracle 里varchar2变量的“空”只有null一种表现形式。初始化、异常分支、入参校验都不要用: 来表达“没有值”直接写: null。判断时用is null/is not null不要用 / 。如果你从 MySQL 迁移 SQL先把所有 和 找出来逐一替换。7.2 条件判断的替代写法除了is not null还可以用nvl给变量一个明确的哨兵值但哨兵值不能是。比如nvl(v_new_dno, NOT_FOUND)然后在判断时比较这个哨兵值。不过这种写法要确保哨兵值不会和真实数据冲突。对于 update 场景最稳妥的还是is not null。7.3 文末 CTA下次再遇到类似 PL/SQL 条件不生效、update 被跳过的问题可以先把代码贴到 TaoToken 模型对话 里用 Codex 的配置通道让模型帮你对照 Oracle 语义。Key 在 控制台 API Keys 创建Codex 的config.toml里base_url保持https://taotoken.net/api。把这次排查流程固定下来比每次靠print变量猜要快得多。