【问题标题】:From XML inside CLOB, to Oracle table with list of paths从 CLOB 中的 XML,到带有路径列表的 Oracle 表
【发布时间】:2015-01-01 09:36:28
【问题描述】:

我使用的 Oracle 版本是:

BANNER
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
PL/SQL Release 10.2.0.4.0 - Production
CORE    10.2.0.4.0  Production
TNS for IBM/AIX RISC System/6000: Version 10.2.0.4.0 - Productio
NLSRTL Version 10.2.0.4.0 - Production

在上一个问题中,我问过如何将 clob 转换为表格,请参见:

From XML to list of paths in Oracle PL/SQL environment

我收到的答案很好,它适用于不太大的 XML。

但是,如果我有一个名为 MY_TABLE_ONE 的表,其中有一个名为 MY_FIELD 的字段,该字段是一个内容非常大的 CLOB(例如 500 KB),则以下语句不会在合理的时间内退出:

CREATE TABLE MY_TABLE_TWO
AS
   WITH PARAMS AS (SELECT   XMLTYPE (MY_FIELD) FROM MY_TABLE_ONE)
   SELECT   ELEMENT_PATH, ELEMENT_TEXT
     FROM   XMLTABLE (
               '              
      for $i in $doc/descendant-or-self::*
        return <element>
                 <element_path> {$i/string-join(ancestor-or-self::*/name(.), ''/'')} </element_path>
                 <element_content> {$i/text()}</element_content>
               </element>  
    '
                  PASSING (SELECT   * FROM PARAMS) AS "doc"
                  COLUMNS ELEMENT_PATH VARCHAR2 (4000) PATH '//element_path',
                          ELEMENT_TEXT VARCHAR2 (4000) PATH '//element_content'
            );

是否有任何替代方法可以更高效地转换存储在 CLOB 列中的 XML,在 Oracle 表中包含路径列表和相应值?

上面的说法是对的,但需要太多时间来敲定。

非常感谢您考虑我的要求。

编辑:

我已经尝试过这个迭代解决方案,但没有成功:-(

BEGIN
   DECLARE
      CURSOR S_CUR
      IS
         WITH PARAMS AS (SELECT   XMLTYPE (MY_FIELD) FROM MY_TABLE_ONE)
         SELECT   ELEMENT_PATH, ELEMENT_TEXT
           FROM   XMLTABLE (
                     '              
      for $i in $doc/descendant-or-self::*
        return <element>
                 <element_path> {$i/string-join(ancestor-or-self::*/name(.), ''/'')} </element_path>
                 <element_content> {$i/text()}</element_content>
               </element>  
    '
                        PASSING (SELECT   * FROM PARAMS where rownum < 101) AS "doc"
                        COLUMNS ELEMENT_PATH VARCHAR2 (4000) PATH '//element_path',
                                ELEMENT_TEXT VARCHAR2 (4000) PATH '//element_content'
                  );

      TYPE FETCH_ARRAY IS TABLE OF S_CUR%ROWTYPE;

      S_ARRAY   FETCH_ARRAY;
   BEGIN
      EXECUTE IMMEDIATE 'ALTER SESSION SET DB_FILE_MULTIBLOCK_READ_COUNT=256';

      EXECUTE IMMEDIATE 'TRUNCATE TABLE GOOFY99 DROP STORAGE';

      OPEN S_CUR;

      LOOP
         FETCH S_CUR
         BULK COLLECT INTO   S_ARRAY
         LIMIT 500;

         FORALL I IN 1 .. S_ARRAY.COUNT
            INSERT                                                  /*+APPEND */
                  INTO GOOFY99
            VALUES   S_ARRAY (I);

         COMMIT;
         EXIT WHEN S_CUR%NOTFOUND;
      END LOOP;

      CLOSE S_CUR;

      COMMIT;
   END;
END;

【问题讨论】:

  • 能否请您在某处发布有问题的大型 XML 文件? (我不想这么说,但我还不知道 XmlQuery 语法,所以我无法从你的选择中对你的 XML 结构进行逆向工程。)
  • XML 包含保留的数据和标签,所以我必须在发布到互联网之前对其进行修改......这不是那么直接!
  • 明白。不着急。
  • 我在 12.1.0.2 EE 上针对构造为 xmltype(cursor(select * from dba_objects)) 的 XML 数据进行了尝试(总共 46 MB 的 XML 数据存储在外联 CLOB 中)。您的第一个查询 CREATE TABLE MY_TABLE_TWO AS ... 在 11.5 秒内运行。我会在 11.2 XE 上尝试同样的方法。
  • 11.2 XE,相同的 XML 结构(总共 6.5 MB 的 XML 数据),创建表运行时间为 90 秒。越来越接近你的问题,我猜。

标签: xml oracle path


【解决方案1】:

UPD 我发现了相当大的 xml 文件 (140 KB)。我的系统:具有核心 i5 处理器(2400 MHz)的笔记本电脑,虚拟机内的 oracle 12c,处理时间 - 0.38 秒。 这种方法是我知道的唯一选择。我在 w3schools.com 上找到的 xml 示例。

declare
  xml_str clob := q'[<?xml version="1.0" encoding="UTF-8"?>
<CATALOG>
 <CD>
  <TITLE>Empire Burlesque</TITLE>
  <ARTIST>Bob Dylan</ARTIST>
  <COUNTRY>USA</COUNTRY>
  <COMPANY>Columbia</COMPANY>
  <PRICE>10.90</PRICE>
  <YEAR>1985</YEAR>
 </CD>
 <CD>
  <TITLE>Hide your heart</TITLE>
  <ARTIST>Bonnie Tyler</ARTIST>
  <COUNTRY>UK</COUNTRY>
  <COMPANY>CBS Records</COMPANY>
  <PRICE>9.90</PRICE>
  <YEAR>1988</YEAR>
 </CD>
</CATALOG>]';

v_doc  dbms_xmldom.domdocument;
node   dbms_xmldom.domnode;
txt    varchar2(4000);
type   t_list is table of number index by varchar2(4000);
v_list t_list;
  procedure enum_nodes(n dbms_xmldom.domnode, tag_name varchar2) is
    chn  dbms_xmldom.domnode;
    nl   dbms_xmldom.domnodelist;
  begin
    nl := dbms_xmldom.getchildnodes(n);
    for i in 0..dbms_xmldom.getlength(nl) loop
      chn := dbms_xmldom.item(nl, i);

      if dbms_xmldom.getnodetype(chn) = 1 then
         enum_nodes(chn, tag_name || dbms_xmldom.getnodeName(chn) || '/');
      elsif dbms_xmldom.getnodetype(chn) = 3 then
         v_list(tag_name || dbms_xmldom.getnodevalue(chn)) := 1;
      end if;
    end loop;
  end;
begin
  v_doc := dbms_xmldom.newdomdocument(xml_str);
  node := dbms_xmldom.makenode(v_doc);

  enum_nodes(node, '/');

  txt := v_list.first;
  while txt is not null loop
     dbms_output.put_line(txt);
     txt := v_list.next(txt);
  end loop;
end;
/

【讨论】:

  • 输出为: /CATALOG/CD/ARTIST/text() /CATALOG/CD/COMPANY/text() /CATALOG/CD/COUNTRY/text() /CATALOG/CD/PRICE/text () /CATALOG/CD/TITLE/text() /CATALOG/CD/YEAR/text() 为什么???
  • 对不起,据我了解,您需要像您的示例中一样的输出:stackoverflow.com/questions/18146788/…。我刚刚拿了另一个xml文件。你需要什么输出?
  • 我需要每个级别的值而不是 text()。
  • 有没有办法将路径和值显示到两个单独的字段中?现在我正在运行我的 500Kbytes XML 来测试代码...
  • @UltraCommit 谢谢! :) 路径 - 在过程 enum_nodes 的变量 tag_name 中,值 - 是 dbms_xmldom.getnodevalue(chn) 的结果。可以单独累积它们,但您需要进行一些更改。在这里,我使用索引表(t_list 类型的变量 v_list)及其属性只有唯一索引。那里只能是一个索引,所以你需要使用另一种结构来累积值,然后选择唯一的。
猜你喜欢
  • 2018-08-08
  • 2013-08-11
  • 2016-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-15
  • 1970-01-01
相关资源
最近更新 更多