在 Oracle table 中将大 xml 文件作为 blob 插入

Insert large xml file as as blob in oracle table

我有一个 450 MB xml 文件,我想将它插入到 Oracle table 的 Blob 列中。我尝试将文件内容作为字符串插入,但它显示 "string literal too long"。任何人都可以建议一种优雅的方式插入 table。 提供:我在数据库服务器中没有目录访问权限,我的本地系统中有 xml 文件

用任何语言编写能够读取文件并向您的 table 中插入查询的小程序如何?

假设您进行了一些搜索,偶然发现了一个 search result:您找到了一个包含 PL/SQL 代码的页面。

CREATE OR REPLACE DIRECTORY test_dir AS '<path_on_db_server>'; 

DECLARE
  l_bfile  BFILE;
  l_blob   BLOB;
BEGIN
  -- this depends on your table definition, col1 being the BLOB column
  INSERT INTO tab1 (col1, col2) VALUES (empty_blob(), 'test1')
  RETURN col1 INTO l_blob;

  l_bfile := BFILENAME('test_dir', 'my.xml');
  DBMS_LOB.fileopen(l_bfile, Dbms_Lob.File_Readonly);
  DBMS_LOB.loadfromfile(l_blob, l_bfile, DBMS_LOB.getlength(l_bfile));
  DBMS_LOB.fileclose(l_bfile);

  COMMIT;
END;

然后你试试(在你把你的 test.xml 文件放在 <path_on_db_server> 的服务器上并确保 oracle 用户可以访问该文件之后)。

您可以将 xml 文件转换为 sql 脚本,执行适当制作的匿名 plsql 块。将此脚本加载到数据库中将填充 blob。

基本思路是将 xml 文件拆分为 2000 个字符的块。第一个块可以直接插入到目标 table 的 blob 列中。彼此将通过利用 dbms_lob.fragment_insert 包过程的更新语句添加。 !!!警告:这不是推荐的做法!。最好让 dba 为您加载它!

例子:

  • 假设:

    • 目标 table 有 2 列,pk 和 blob。
    • pk 是 42。
    • 2000 是被视为合适的样本块大小table。从技术上讲,dbms_lob.fragment_insert 最多可处理 32767,但是,涉及的其他工具(例如 sqlplus)可能对行长度有更严格的限制。
  • 代码:

    declare
        l_b BLOB;
    begin
        insert
          into
      t_target ( c_pk, c_blob )
        values ( 42, utl_raw.cast_to_raw('<This literal contains the first 2000 (chunksize) characters of the xml file>') )
     returning c_blob
         into l_b
            ;
    
     dbms_lob.fragment_insert ( l_b, 2000, 1+dbms_lob.getlength(l_b), utl_raw.cast_to_raw('<This literal contains 2000 characters starting at [0-based] offset 2000>'));
     dbms_lob.fragment_insert ( l_b, 2000, 1+dbms_lob.getlength(l_b), utl_raw.cast_to_raw('<This literal contains 2000 characters starting at [0-based] offset 4000>'));
     dbms_lob.fragment_insert ( l_b, 2000, 1+dbms_lob.getlength(l_b), utl_raw.cast_to_raw('<This literal contains 2000 characters starting at [0-based] offset 6000>'));
     ...
     dbms_lob.fragment_insert ( l_b, 2000, 1+dbms_lob.getlength(l_b), utl_raw.cast_to_raw('<This literal contains the last chunk>'));
    
     commit;
    end;
    /
    show err
    

准备工作

  1. 您需要确保 xml 文件中没有出现单引号。 否则生成的 plsql 代码将包含语法错误。

    如果不使用单引号作为属性值分隔符,只需将其替换为数字实体 &x#28;.

  2. 创建批量匿名plsql

定期向文件中插入数据的方法在this SO question, the most flexible approach being outlined in this answer中介绍。插入以下字符串而不是换行符:

"'));\n     dbms_lob.fragment_insert ( l_b, 2000, 1+dbms_lob.getlength(l_b), utl_raw.cast_to_raw('"

剩下的匿名plsql可以手工copied/written

警告

照原样,脚本大小将与原始 xml 大小相同,而 plsql 块将包含 200k+ 行。您很可能会 运行 了解所涉及工具的一些局限性。但是,脚本可以分成任意数量的块,如下所示:

declare
    l_b BLOB;
begin
    select c_blob
      into l_b
      from t_target
     where c_pk = 42
        ;

 dbms_lob.fragment_insert ( l_b, 2000, 1+dbms_lob.getlength(l_b), utl_raw.cast_to_raw('<This literal contains 2000 characters starting at [0-based] offset <k>*2000>'));
 dbms_lob.fragment_insert ( l_b, 2000, 1+dbms_lob.getlength(l_b), utl_raw.cast_to_raw('<This literal contains 2000 characters starting at [0-based] offset (<k>+1)*2000>'));
 dbms_lob.fragment_insert ( l_b, 2000, 1+dbms_lob.getlength(l_b), utl_raw.cast_to_raw('<This literal contains 2000 characters starting at [0-based] offset (<k>+2)*2000>'));
 ...
 dbms_lob.fragment_insert ( l_b, 2000, 1+dbms_lob.getlength(l_b), utl_raw.cast_to_raw('<This literal contains 2000 characters starting at [0-based] offset (<k>+<n_k>)*2000>'));
end;
/
show err

再一次:!!!警告:这不是推荐的做法!。最好让 dba 为您加载它!