根本原因是varchar2(10)按字节限制(al32utf8下汉字占3字节),"你好世界"4个汉字占12字节,超出10字节上限,触发ora-06502;oracle拒绝静默截断,需用lengthb()校验字节数而非length()。

根本原因不是字符串“长”,而是接收容器声明长度小于运行时实际字节数。 编译器不校验值长度,只检查语法和对象存在性;错误总在赋值、转换或隐式拼接那一瞬间爆发。
为什么VARCHAR2(10)接'你好世界'就崩
中文在AL32UTF8字符集下占3字节/字符,“你好世界”4个字符 → 实际12字节,超出VARCHAR2(10)的10字节上限。Oracle拒绝静默截断,直接抛ORA-06502: character string buffer too small。
- 用
LENGTHB('你好世界')查真实字节数,别信LENGTH()返回的字符数 - 查字符集:
SELECT VALUE FROM V$NLS_PARAMETERS WHERE PARAMETER = 'NLS_CHARACTERSET' - 声明变量时,按字节而非字符预估:比如存中文,
VARCHAR2(30)最多只够10个汉字(AL32UTF8)
TO_CHAR()和拼接是隐形放大器
看似安全的转换或拼接,常因隐式行为突破长度边界。例如TO_CHAR(SYSDATE)依赖NLS_DATE_FORMAT,默认格式可能生成22字符;v_out VARCHAR2(5) := p_in || '_ext'在p_in为'ab'时结果是'ab_ext'(6字符)——已超限。
- 所有
TO_CHAR()必须显式指定格式:TO_CHAR(sysdate, 'YYYY-MM-DD') - 数值转字符串也要控长:
TO_CHAR(my_num, 'FM999999990.00'),避免默认科学计数法或补空格 - 拼接前用
LENGTHB()预判:IF LENGTHB(p_in) + 4 > 10 THEN ...
%TYPE变量也会失效
v_name employees.last_name%TYPE看似健壮,但若表字段被ALTER TABLE MODIFY last_name VARCHAR2(200)加长,而存储过程未重编译,仍沿用旧长度定义,运行时照样报错。
- 每次修改表结构后,手动
ALTER PROCEDURE my_proc COMPILE - 对关键字段,用
USER_TAB_COLUMNS核对当前定义:SELECT DATA_LENGTH FROM USER_TAB_COLUMNS WHERE TABLE_NAME='EMPLOYEES' AND COLUMN_NAME='LAST_NAME' - 不确定长度的输出(如日志、描述),直接声明为
CLOB,而非放大VARCHAR2
调用端registerOutParameter长度不匹配
Java/.NET调用存储过程时,如果registerOutParameter(idx, Types.VARCHAR, 10)设成10,但PL/SQL里该OUT参数是VARCHAR2(100),驱动层会在进过程前就截断,甚至不进过程就报ORA-06502。
- 调用端声明长度必须 ≥ PL/SQL中变量声明长度
- 若PL/SQL用
CLOB,调用端必须用registerOutParameter(idx, Types.CLOB),不能靠类型推断 - 避免在JDBC里传
setNull(idx, Types.VARCHAR)再进PL/SQL做TO_CHAR()——NULL会直接触发ORA-06502
最易被忽略的是多层嵌套:函数A返回VARCHAR2(50),被赋给B过程里声明为VARCHAR2(30)的变量,再参与拼接。这种链式传递中,光看B的声明没用,得顺着调用栈逐层查LENGTHB()输出。临时加DBMS_OUTPUT.PUT_LINE('len=' || LENGTHB(v_var))比翻十页代码快得多。











