不能将clob/blob当普通字符串或字节数组使用,必须通过dbms_lob初始化并分块读写,否则易致乱码、截断或ora-24801/ora-22285错误;clob的offset和amount单位为字符(al32utf8下中文占1字符3字节),blob则为字节,混用是乱码主因;单次读写上限32767字节,超长需循环分批;空lob须先用empty_clob()/empty_blob()或dbms_lob.createtemporary()初始化;dbms_lob.converttoblob不转换字符集,仅原样拷贝字节;长clob不可硬编码入sql,应绑定变量或dbms_lob.writeappend分段写入。

直接说结论:不能把 CLOB/BLOB 当普通字符串或字节数组用,必须走 DBMS_LOB 初始化 + 分块读写路径,否则大概率乱码、截断或报 ORA-24801/ORA-22285。
DBMS_LOB.READ 的 offset 和 amount 单位不统一
CLOB 和 BLOB 的 offset 与 amount 含义完全不同,混用是绝大多数乱码的根源:
- CLOB:
offset是从 1 开始的**字符位置**,amount是要读的**字符数**(不是字节数);AL32UTF8 下一个中文算 1 字符,占 3 字节 - BLOB:
offset和amount都是**字节位置和字节数**;若内容是 UTF-8 文本,得先用UTL_RAW.LENGTH或LENGTHB查真实字节长度,再传入amount - 常见错误:对 CLOB 用
LENGTH('中文')得到 2,再当字节数传给 BLOB 的READ—— 实际只读 2 字节,必然卡在 UTF-8 中间字节,返回乱码或ORA-24801 - 单次读取上限为 32767 字节(
VARCHAR2/RAW最大长度),超长必须循环分批,每次amount ≤ 32767
向空 CLOB/BLOB 写入前必须初始化
声明 clob_var CLOB 只是一个空 locator 引用,不是可写的 LOB 实例。直接调 DBMS_LOB.WRITE_APPEND 会静默失败或报 ORA-22285 / ORA-22289:
- INSERT 场景:必须用
EMPTY_CLOB()或EMPTY_BLOB()占位,再SELECT ... INTO clob_var FROM t WHERE ... FOR UPDATE锁定行 - 临时 LOB 场景:必须显式调
DBMS_LOB.CREATETEMPORARY(clob_var, TRUE),第二个参数TRUE表示启用缓存(推荐),写完记得DBMS_LOB.FREETEMPORARY(clob_var),否则 PGA 内存泄漏 - 别对刚
SELECT出来的 LOB 调DBMS_LOB.OPEN(..., DBMS_LOB.LOB_READONLY)后再写 —— 只读打开后WRITE_APPEND必报ORA-22289
DBMS_LOB.CONVERTTOBLOB 不做字符集转换
这个函数名有严重误导性:DBMS_LOB.CONVERTTOBLOB 只是把 CLOB 当作字节序列原样拷贝进 BLOB,不做任何编码转换:
- 如果数据库字符集是 AL32UTF8,CLOB 中 “你好” 存的是 UTF-8 编码
0xE4BDA0E5A5BD(6 字节),转出的 BLOB 就是这 6 字节 - 若下游系统期望 GBK 编码(“你好” 在 GBK 中是
0xC4E3BAC3,4 字节),直接用这个 BLOB 就会显示乱码 - 真要转码,得先用
UTL_I18N.STRING_TO_RAW('你好', 'GBK')得到目标编码的RAW,再用DBMS_LOB.WRITE写入 BLOB
长 CLOB(>32767 字符)不能硬编码进 SQL
超过 PL/SQL 变量上限的内容,无法通过字面量或简单绑定变量一次性传入:
- INSERT 或 UPDATE 语句中直接写
'...' || '...' || ...拼接长文本,会触发ORA-01461(不能将 LONG 值绑定到此列)或静默截断 - 安全做法:用绑定变量传入,或拆成多段,用
DBMS_LOB.WRITEAPPEND分多次追加,每次传入VARCHAR2(≤32767 字符) - 客户端上传时,避免一次性构造完整 CLOB 字符串;优先流式分块传参,由存储过程逐段写入
最易被忽略的点是单位混淆——CLOB 的 “第 10 个字符” 和 BLOB 的 “第 10 个字节” 在 UTF-8 下可能指向完全不同的数据边界;一旦错配,读出来的永远是半截中文或乱码,且错误信息(如 ORA-24801)不提示根本原因。











