6

我正在为报告工具(Pentaho Report Designer)编写查询,我需要在其中检索存储在 Oracle 11 数据库中的一些 jpg。诀窍是慢跑(存储为 BLOB)包括一个 12 字节的标头(由另一个应用程序添加),我需要删除它。报告工具还要求将数据作为 RAW 数据返回。

我遇到了一些问题,我发现的用于操作 BLOB 的函数/过程似乎都对它们有禁止的大小/长度限制。

这个查询尽可能接近工作,使用 DBMS_LOB.SUBSTR(dbfile.filedata,2000,12):

select DBMS_LOB.SUBSTR(dbfile.filedata,2000,12) as filedata
from bms_0002005_251 safety
inner join bms_9999999_100 file02 on safety.bms_id = file02.bms_fk_0002005_839_ID
inner join bms_9999999_104 inc on safety.bms_fk_0002005_844_id = inc.bms_id
left join bms_dbfiles dbfile on file02.bms_9999999_40 = dbfile.uniqueid

对于 <= 2000 字节的图像,这可以完美运行,去除 12 字节标头并返回原始数据,如 FFD8FFE000104A46494600010201006000600000FFEE000E41646F626500640000000001...等

但是对于较大的图像(大部分图像)2000 是不够的,但是只要我将子字符串长度增加到 2001,查询就会失败:

ORA-06502: PL/SQL: numeric or value error: raw variable length too long ORA-06512: at line 1 06502. 00000 - "PL/SQL: numeric or value error%s"

这是我得到的最接近的,但长话短说 - 有没有什么方法可以在单个查询中从大 BLOB 中删除前 12 个字节并将数据作为 RAW 返回?

4

2 回答 2

1

一般来说,考虑到ORA-14553. Blob 变量是指向数据的指针,而不是数据本身。这就是为什么DBMS_LOB.SUBSTRreturn not BLOBor CLOB, but RAWor CHAR- 等函数的原因,因为最后一种类型的数据(变量)在 RAM 中直接可用。

因此,要查询修改后的 blob(从头部截去 12 个字节),我们需要在查询之前创建并存储修改后的 blob。根据业务需求,它可以就地完成,也可以通过创建新的 blob 来完成,保留原件。在这个问题的情况下,我想我们不能就地修改原始 blob。

显然,第二种方式(保留原件)是资源密集型方式。

解决方案大图:

  1. 确定要查询的 blob 列表
  2. 对于列表中的每个项目,创建并存储一个修改过的 blob(例如,修剪后的 12 个字节),并通过 id 链接到原始
  3. 运行返回修改后的 blob 的查询
  4. 删除修改的 blob(在查询游标被消耗或按计划后立即)

这导致的问题多于解决的问题:

  • 需要清理修改过的 blob
  • 原版和修改版同步
  • 存储数据量翻倍
  • 其他惊喜

我想有一个不那么痛苦的解决方案 - 在外部方编写代码,使用查询结果并修改数据库外部的 blob 数据

于 2017-06-15T13:22:31.463 回答
1

只需创建一个用户定义的函数来为您执行此操作并在选择中使用它。

我通过简单地将第一个偏移量设置为 13 而不是 1 来制作一个简单的示例:

create or replace function strip12(p_blob in blob) return blob is
  pragma autonomous_transaction;
  l_length  pls_integer;
  l_loc     pls_integer;
  l_buffer  pls_integer := 2000;
  l_newblob blob;
begin
  dbms_lob.createtemporary(lob_loc => l_newblob, cache => true);
  l_length := dbms_lob.getlength(p_blob);

  l_loc := 13;
  while l_loc <= l_length loop
    dbms_lob.append(dest_lob => l_newblob, src_lob => dbms_lob.substr(lob_loc => p_blob, amount => l_buffer, offset => l_loc));
    l_loc := l_loc + l_buffer;
  end loop;
  return l_newblob;
end;

然后你只需在你的选择中使用它

select strip12(dbfile.filedata) as filedata
  from bms_0002005_251 safety
 inner join bms_9999999_100 file02
    on safety.bms_id = file02.bms_fk_0002005_839_ID
 inner join bms_9999999_104 inc
    on safety.bms_fk_0002005_844_id = inc.bms_id
  left join bms_dbfiles dbfile
    on file02.bms_9999999_40 = dbfile.uniqueid

问候

于 2019-07-24T12:28:19.990 回答