DBMS_LOB.READ中CLOB的offset和amount按字符计算(offset从1起、amount为字符数),BLOB则按字节计算;混用导致乱码或ORA-24801,必须分块读写且单位严格区分。
dbms_lob 不能直接“流式”读写,必须分块处理,且 clob/blob 的 offset/amount 单位完全不同——这是绝大多数乱码、截断、ora-24801 错误的根源。
DBMS_LOB.READ 的 offset 和 amount 到底按什么单位算
不是统一按字节,也不是统一按字符:CLOB 和 BLOB 完全不同。
- CLOB:
offset是从 1 开始的**字符位置**,amount是要读的**字符数**(不是字节数);若数据库字符集为AL32UTF8,一个中文占 3 字节,但只算 1 个字符 - BLOB:
offset和amount都是**字节位置和字节数**;若内容是 UTF-8 编码文本,必须先用UTL_RAW.LENGTH或LENGTHB查实际字节长度,再传入amount - 常见错误:把 CLOB 当 BLOB 用同一套逻辑调
DBMS_LOB.READ,比如offset := 10; amount := LENGTH('中文')——对 CLOB 是对的,对 BLOB 就会跳过前 10 字节后只读 2 字节,必然乱码或 ORA-24801 - 最大单次读取受限于 PL/SQL 变量上限:
VARCHAR2最多 32767 字节,RAW同样;超长内容必须循环分批读,每次amount ≤ 32767
向空 CLOB/BLOB 写入前必须初始化
声明一个 clob_var CLOB 变量 ≠ 拥有一个可写的 LOB 实例。它只是空 locator 引用,直接 WRITE_APPEND 会静默失败或报 ORA-22285/ORA-22289。
- INSERT 场景:用
EMPTY_CLOB()占位,再SELECT clob_col INTO clob_var FROM t WHERE ... FOR UPDATE锁定行,之后才能写 - 临时 LOB 场景:必须显式调
DBMS_LOB.CREATETEMPORARY(clob_var, TRUE),第二个参数TRUE表示可被缓存(推荐) - 不要对刚 SELECT 出来的 LOB 调
DBMS_LOB.OPEN(..., DBMS_LOB.LOB_READONLY)后再写——只读打开后WRITE_APPEND必报 ORA-22289 -
DBMS_LOB.WRITE要求指定起始位置(字符或字节),而WRITE_APPEND总是从末尾追加,更安全,但前提是 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先转成目标编码的RAW,再用DBMS_LOB.WRITE写入 BLOB - 长 CLOB(如 > 32767 字符)不能硬编码进 SQL,必须用绑定变量或分段
DBMS_LOB.WRITE;否则 INSERT/UPDATE 语句会因字面量超限报错
为什么 BFILE 不能用 WRITE_APPEND
BFILE 是只读外部文件定位器,不参与事务,所有写操作在语法上就不被允许。
-
DBMS_LOB.WRITE_APPEND对 BFILE 会直接报 ORA-22289:“cannot perform WRITE_APPEND on an unopened file or LOB” - 哪怕你用
DBMS_LOB.FILEOPEN打开了 BFILE,也仅支持READ,不支持任何写接口 - 想“修改”外部文件?只能靠操作系统命令(如
UTL_FILE写新文件 + 替换),DBMS_LOB 不负责这个 - 使用 BFILE 前必须确保 DIRECTORY 已创建、权限已授予、路径大小写敏感(Linux 下
/DATA≠/data)
最易被忽略的是:CLOB 的 offset=1 和 BLOB 的 offset=1 指的完全不是同一个“1”。写通用封装函数时,必须用类型判断分支,不能共用一套参数计算逻辑;临时 LOB 的 CREATETEMPORARY 调用不可省略,否则后续所有写操作都无效。

















