ITPub博客

首页 > 数据库 > Oracle > 第76期 明明Oracle表LCADDRESS列定义VARCHAR2(80)ORA-12899报错值太大

第76期 明明Oracle表LCADDRESS列定义VARCHAR2(80)ORA-12899报错值太大

原创 Oracle 作者:pxbibm 时间:2026-07-29 09:19:15 0 删除 编辑

明明Oracle表LCADDRESS的 "COMPANYADDRESS"列定义VARCHAR2(80),但是开发再执行update语句时,还是报错,ORA-12899报错"COMPANYADDRESS" 的值太大 (实际值: 82, 最大值: 80),开发同事使用update更新数据时使用如下sql时收到了ORA-12899的错误。update的sql进行了简化。

 update LCADDRESS set 
 COMPANYADDRESS = SUBSTR(
        CASE
        WHEN COMPANYADDRESS LIKE '%区%' THEN SUBSTR(COMPANYADDRESS , 1, INSTR(COMPANYADDRESS , '区') + 1) || '测试路111号'
        WHEN COMPANYADDRESS LIKE '%县%' THEN SUBSTR(COMPANYADDRESS , 1, INSTR(COMPANYADDRESS , '县') + 1) || '测试路111号'
        WHEN COMPANYADDRESS LIKE '%市%' THEN SUBSTR(COMPANYADDRESS , 1, INSTR(COMPANYADDRESS , '市') + 1) || '测试路111号'
        ELSE '辽宁省大连市沙河口区测试路111号'
        END,
        1,
        80
)
where 【你的条件】;[object Object]

问题根源

报错: 实际长度 82,字段 VARCHAR2 (80) 最多存 80 字符,但是开发同事 的逻辑拼接后字符串偶尔超出 80 位。
SUBSTR(CASE ... END,1,80)

⚠️ 误区:Oracle SUBSTR(字符,1,80) 是 字符个数,看着限制 80;

但是!拼接过程中间产物有可能先超出,或者你存在 中文 + 符号组合计算偏差?仔细看:

SUBSTR( 【拼接结果】 ,1,80)

理论上最终结果≤80,为什么还报 12899?

两种真实场景:

场景 1:拼接后的字符串本身超长,SUBSTR 外层包裹位置写错(你当前写法语法没问题,但有一个坑)

SUBSTR(
    CASE ... || '测试路111号'
    END,1,80)
语法没问题,但有另一种极易踩坑情况: 数据库字符集为 AL32UTF8,VARCHAR2 (80) 默认是字节!不是字符!
重中之重!
VARCHAR2(80)
如果建表语句没有指定 CHAR:
COMPANYADDRESS VARCHAR2(80)  -- 默认字节 byte

一个中文 UTF8 占 3 字节。当你写 SUBSTR(xxx,1,80) 是 按字符截取。假设截取 80 个汉字,80*3=240 字节,远远超过 80 字节 → ORA-12899!这就是你报错最可能的根因!

场景 2:少数情况:instr 定位拼接后刚好临界溢出

示例:原始地址截取到「区 / 县 / 市」+1,再拼接 测试路111号,刚好产生超长字符串。

验证字符集定义

-- 查看字段定义(区分byte/char)
SELECT COLUMN_NAME,DATA_TYPE,DATA_LENGTH,CHAR_USED
FROM USER_TAB_COLUMNS
WHERE 
TABLE_NAME='LCADDRESS' AND COLUMN_NAME='COMPANYADDRESS';

  • CHAR_USED = B → 按字节存储(高危,你现在大概率是这个)
  • CHAR_USED = C → 按字符存储

方案一【优先推荐,修改 SQL,按字节截断兼容 byte 模式】

如果你暂时不能改表结构,把外层截断改成 按 字节截断,使用 UTL_RAW.CAST_TO_RAW + SUBSTRB
COMPANYADDRESS = UTL_RAW.CAST_TO_VARCHAR2(
    SUBSTRB(UTL_RAW.CAST_TO_RAW(
        CASE
        WHEN COMPANYADDRESS LIKE '%区%' THEN SUBSTR(COMPANYADDRESS , 1, INSTR(COMPANYADDRESS , '区') + 1) || '测试路111号'
        WHEN COMPANYADDRESS LIKE '%县%' THEN SUBSTR(COMPANYADDRESS , 1, INSTR(COMPANYADDRESS , '县') + 1) || '测试路111号'
        WHEN COMPANYADDRESS LIKE '%市%' THEN SUBSTR(COMPANYADDRESS , 1, INSTR(COMPANYADDRESS , '市') + 1) || '测试路111号'
        ELSE '辽宁省大连市沙河口区测试路111号'
        END
    ), 1, 80))
SUBSTRB 针对字节截断,保证最终占用字节 ≤80,杜绝 ORA-12899。

方案二【治本,修改表定义(长期建议)】

把字段改成按字符定义,开发最省心:
ALTER TABLE LCADDRESS MODIFY COMPANYADDRESS VARCHAR2(80 CHAR);
修改后, SUBSTR(...,1,80) 按字符截断,不会出现中文多字节溢出。

方案三:调整业务逻辑,提前缩短拼接内容(不推荐,改动业务规则)

缩短后缀常量,或者提前裁剪原始地址,保证拼接后长度可控。

优化你原始 SQL 的小 bug(INSTR 风险)

如果字段 ** 不存在「区 / 县 / 市」之外进入 else 没问题,但有一种边界:

INSTR(COMPANYADDRESS,'区')+1 逻辑示例:

沙河口区 → INSTR 找到 “区” 位置 N,SUBSTR 取 1~N+1,包含 “区”;然后拼接后缀。

逻辑本身没问题。

完整可直接执行 UPDATE 语句范本(字节安全版)

UPDATE LISMAN.LCADDRESSSET COMPANYADDRESS = UTL_RAW.CAST_TO_VARCHAR2(
    SUBSTRB(UTL_RAW.CAST_TO_RAW(
        CASE
            WHEN COMPANYADDRESS LIKE '%区%' THEN SUBSTR(COMPANYADDRESS , 1, INSTR(COMPANYADDRESS , '区') + 1) || '测试路111号'
            WHEN COMPANYADDRESS LIKE '%县%' THEN SUBSTR(COMPANYADDRESS , 1, INSTR(COMPANYADDRESS , '县') + 1) || '测试路111号'
            WHEN COMPANYADDRESS LIKE '%市%' THEN SUBSTR(COMPANYADDRESS , 1, INSTR(COMPANYADDRESS , '市') + 1) || '测试路111号'
            ELSE '辽宁省大连市沙河口区测试路111号'
        END
    ), 1, 80))WHERE 【你的条件】

快速总结

  1. 90% 概率原因: 字段 VARCHAR2 (80) 是字节模式,中文占用多字节,按字符 substr (1,80) 依然超字节上限;

  2. 临时解决方案:使用 SUBSTRB 按字节截断;

  3. 根治方案:修改字段为 VARCHAR2(80 CHAR);

  4. 不要单纯依赖外层 SUBSTR(xx,1,80) 在字节模式下防止超长。

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/12798004/viewspace-3176987/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
Oracle ACE。ACOUG核心成员,Oracle用户组年轻专家。熟悉Oracle的体系结构,擅长Oracle数据库架构规划、SQL优化、OWI方面性能的优化。曾服务于航天、航空、机车、大型制造行业数据库的维护以及系统开发工作,开发过基于Oracle数据库的大型ERP系统。
  • 博文量
    345
  • 访问量
    2477875