最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
如何在Oracle存储过程中读取和写入BLOB?
时间:2026-08-19 09:54:48 编辑:袖梨 来源:一聚教程网
Oracle BLOB字段插入更新必须分三步:先用EMPTY_BLOB()占位,再SELECT...FOR UPDATE获取可写定位器,最后通过DBMS_LOB.WRITEAPPEND等过程写入字节;跳过任一环节将触发ORA-01461、ORA-22275等错误。
直接用 INSERT INTO t (blob_col) VALUES ('...') 或 UPDATE t SET blob_col = :data 会失败——Oracle 不接受字符串字面量或变量直赋 BLOB,必须走 LOB 定位器路径。跳过 EMPTY_BLOB() 占位、FOR UPDATE 锁定、DBMS_LOB 写入三步中的任意一环,都会触发 ORA-01461、ORA-22275 或静默截断。
写入 BLOB:必须分三步走,缺一不可
你不是在“插入值”,而是在“获取一个可写的 LOB 句柄并往里追加字节”。常见错误是试图一步到位,比如在 INSERT 中传 RAW 或 VARCHAR2 字符串。
-
第一步:占位 —— 用
INSERT INTO t (id, blob_col) VALUES (1, EMPTY_BLOB()),不能传任何实际二进制数据;否则报ORA-01461: can bind a LONG value only for insert into a LONG column -
第二步:锁定并获取句柄 —— 立即执行
SELECT blob_col INTO l_blob FROM t WHERE id = 1 FOR UPDATE;漏掉FOR UPDATE就报ORA-22275: invalid LOB locator specified -
第三步:写入 —— 对
l_blob调用DBMS_LOB.WRITEAPPEND(l_blob, LENGTH(v_raw), v_raw);每次最多写32767字节(VARCHAR2或RAW上限),超长必须循环分块
读取 BLOB:避免一次性加载,优先用分块拷贝
把整个 BLOB 赋给 PL/SQL 变量(如 l_blob BLOB)只是复制定位器,不搬数据;但若用 DBMS_LOB.SUBSTR 或 UTL_RAW.CAST_TO_VARCHAR2 直接转字符串,极易触发 ORA-22835: buffer too small(因 VARCHAR2 最大 32767 字节,且 CLOB/BLOB 转换有隐式长度限制)。
- 安全读取大 BLOB 的推荐方式是
DBMS_LOB.COPY(dest_lob, src_lob, amount, dest_offset, src_offset),把指定段拷到另一个临时 LOB 或 OUT 参数中,客户端再分批拉取 - 若需提取原始字节做处理(比如校验、加密),用
DBMS_LOB.READ(src_lob, amount, offset, buffer),其中buffer类型必须是RAW(32767);注意offset从 1 开始,不是 0 - 千万别用
UTL_RAW.CAST_TO_VARCHAR2(DBMS_LOB.SUBSTR(...))处理超过 4000 字节的内容——SUBSTR返回RAW,但CAST_TO_VARCHAR2会受 VARCHAR2 长度限制,且非 ASCII 字节可能乱码
临时 LOB:用完必须释放,否则内存泄漏
当你用 DBMS_LOB.CREATETEMPORARY(l_blob, TRUE) 创建可读写的临时 LOB(比如拼接多个 BLOB、解密后暂存),它不落盘,只在 PGA 中存在。这类 LOB 不受事务控制,也不自动清理。
- 必须显式调用
DBMS_LOB.FREETEMPORARY(l_blob),否则同一会话反复调用会吃光 PGA 内存 -
cache => TRUE(第二个参数)表示启用缓存加速访问,对频繁读写的临时 LOB 是必要的;设为FALSE会导致性能骤降 - 临时 LOB 不能用于
INSERT ... SELECT或跨过程传递——它只在当前 PL/SQL 单元生命周期内有效
批量更新 BLOB 字段:慎用 CAST 和 REPLACE
想在 BLOB 里做文本替换(比如改配置文件里的 URL)?别直接套 UTL_RAW.CAST_TO_VARCHAR2 + REPLACE + UTL_RAW.CAST_TO_RAW。BLOB 不是文本,编码、换行、二进制边界全不可控,且 CAST_TO_VARCHAR2 在 >4000 字节时必然报 ORA-22835。
- 真正可行的路径是:先用
DBMS_LOB.CONVERTTOCLOB把 BLOB 按指定字符集转成 CLOB(需确认原始内容确实是该编码的纯文本),再在 CLOB 上操作,最后用DBMS_LOB.CONVERTTOBLOB转回 - 如果只是简单字节替换(比如改 magic number),可用
UTL_RAW.REPLACE,但输入输出都必须是RAW,且整块 BLOB 必须能载入RAW变量(≤32767 字节);超长需分段READ→REPLACE→WRITE - 任何涉及 BLOB 全量转换的操作,在百万级记录上都会极慢;生产环境应尽量避免,改由应用层或外部工具处理
最易被忽略的一点:LOB 操作的事务语义和普通列不同。DBMS_LOB.WRITEAPPEND 修改的是 LOB 数据本身,但它的效果在 COMMIT 后才持久化;而 FOR UPDATE 锁定的是整行,不是 LOB 段——这意味着并发写入同一 LOB 时,仍可能因顺序错乱导致数据损坏,必要时得加应用层互斥。