【问题标题】:repeatable blocks with range in oracleoracle中具有范围的可重复块
【发布时间】:2013-05-04 12:28:55
【问题描述】:

请查看下表。整个数据在 DOCSTART 和 DOCEND 内。数据进一步包含在 BACCSTART 和 BACCEND 中。这种类型的块是可重复的。我必须为 BACCSTART 和 BACCEND 中的每个块选择任何可重复的 ABCD 和 TOTAL(每个块发生一次)和 ACCNAME(每个块发生一次),并形成一个类似

的 xml
<BACCSTART>
<TOTAL>100</TOTAL>
<ABCD>abcd</ABCD>
<ACCNAME>name</ACCNAME>
</BACCSTART>

对于每个这样的块。目前我正在使用 for 循环,但性能达不到标准。它将有大约 200 个这样的块,我必须在 15 秒内形成 xml。目前 for 循环大约需要 53 秒。

ROWNUM   NAME     VALUE
 1      DOCSTART  null
 2      BACCSTART null
 3      ABCD      abcd
 4      ABCD      abcd2
 5      PQRS      pqrs
 6      PQRS      pqrs2
 7      TOTAL     100
 8      ACCNAME   name
 9      BACCEND   null
 10     BACCSTART null
 11     ABCD      abcd
 12     ABCD      abcd2
 13     PQRS      pqrs3
 14     PQRS      pqrs4
 15     TOTAL     150
 16     ACCNAME   name
 17     BACCEND   null
 18     DOCEND    null

请帮我解决这个问题。如果通过时间有效的查询可以做到这一点。

是oracle 10g。

我刚刚对需求进行了修改。 ACCNAME 的值存在于另一个大约 90 行的配置表中。从那里我必须拿起三个值,比如 ACCNAME 的 value1 和相应的 value2 和 value3 并作为标签填充到 xml 中。如果我加入该表的性能会影响。请建议。配置表是这样的

HEADER1      HEADER2        HEADER3        HEADER4 ........
<ACCNAME>    value2          value3
.............
.............
.............
.............

我上面提供的数据,我从一个包含它的表中得到它。将其转换为 clob 并使用 xmltable 后,我得到了上述数据。在继续之前使用全局临时表插入数据会更好吗?喜欢

insert  into data(row_num,name,value)
select /*+ no_xml_query_rewrite , parallel(x,8) */ rownum rn, substr(extractvalue(x.column_value,'/e'),1,instr(extractvalue(x.column_value,'/e'),' ')-1) name,
 substr(extractvalue(x.column_value,'/e'),instr(extractvalue(x.column_value,'/e'),' ')+1)  value
 from dual,
xmltable('e' passing xmltype('<e><e>' || 
replace(long2clob('select longdata from billedacc order by segment_number'), 
'|'||chr(10), '</e><e>')    ||  '</e></e>').extract('e/e')
                       ) x;

还建议是否有更好的方法来处理这个问题。

【问题讨论】:

  • Oracle 的哪个版本? Oracle 在每个主要版本(和一些重点版本)中都增加了其 XML 功能。

标签: xml oracle hierarchy aggregation


【解决方案1】:

给定 9000 个数据行(500 * 您在上面提供的 18 个示例行),以下代码将在大约一秒钟内运行,这应该会达到您的性能目标:

DECLARE

  l_xml_data XMLTYPE;

BEGIN

  <<baccstart_loop>>
  FOR r_baccstart IN (
    -- Initially, get all start/end rows
    WITH delimiters_all_rows AS (
      SELECT d.*
      FROM data d
      WHERE d.name IN ('BACCSTART', 'BACCEND')
    )
    -- Lead over the rows order by row_num to get the start/end pairs 
    -- into a single row
    , delimiters_all_rows_joined AS (
      SELECT 
        dar.row_num baccstart_row_num
      , LEAD(dar.row_num) OVER (ORDER BY row_num) baccend_row_num
      , dar.name
      FROM delimiters_all_rows dar
    )
    -- Eliminate the BACCEND rows as we have their row_num values
    SELECT *
    FROM delimiters_all_rows_joined darj
    WHERE name = 'BACCSTART'
  )
  LOOP

    -- Now get all relevant rows for this data block
    WITH data_rows AS (
      SELECT d.*
      FROM data d  
      WHERE d.row_num > r_baccstart.baccstart_row_num AND d.row_num < r_baccstart.baccend_row_num
      AND (
        d.name IN ('TOTAL','ACCNAME')
        OR (d.name = 'ABCD' AND d.row_num = (
          SELECT MIN(d2.row_num)
          FROM data d2
          WHERE d2.row_num > r_baccstart.baccstart_row_num AND d2.row_num < r_baccstart.baccend_row_num
          AND d2.name = 'ABCD'
        ))
      )
    )
    -- Agg the rows into a single XML block (note: EVALNAME for dynamic XML element name)
    SELECT 
      XMLELEMENT("BACCSTART"
      , XMLAGG(
          XMLELEMENT(EVALNAME(dr.name), dr.value)
          ORDER BY DECODE(dr.name, 'TOTAL', 1, 'ABCD', 2, 3)
        )
      ) xml_data
    INTO l_xml_data
    FROM data_rows dr;

    INSERT INTO data_xml VALUES (l_xml_data);

  END LOOP baccstart_loop;

END; 

我试图让它在一个查询中完成所有工作,但是一旦你应用了这么多行的GROUP BYXMLAGG 的性能似乎就会下降(我已经看到发生的时间和再一次)。当XMLAGG 被限制为尽可能少的行时,性能变得更容易接受。

生成的 XML 被插入到一个名为 data_xml 的表中(这样我就可以很容易地看到结果),但是一旦生成了 XML 片段,您就可以替换它们需要做的任何事情!

我建议您是否将数据插入到表中,然后只需执行 INSERT/SELECT 而不是 SELECT INTO 后跟 INSERT,因为缺少它会更快SQL 和 PL/SQL 之间的上下文切换。

以下 DDL/DML 用于设置此示例:

CREATE TABLE data (
  row_num INTEGER
, name    VARCHAR2(10)
, value   VARCHAR2(10)
)
/

CREATE TABLE data_xml (
  xml_data XMLTYPE
)
/

CREATE SEQUENCE data_seq
START WITH 1
INCREMENT BY 1
CACHE 1000
/

BEGIN

  FOR idx IN 1 .. 500 LOOP

    INSERT INTO data (row_num, name, value) 
    VALUES (data_seq.NEXTVAL, 'DOCSTART', NULL);

    INSERT INTO data (row_num, name, value) 
    VALUES (data_seq.NEXTVAL, 'BACCSTART', NULL);

    INSERT INTO data (row_num, name, value) 
    VALUES (data_seq.NEXTVAL, 'ABCD', 'abcd');

    INSERT INTO data (row_num, name, value) 
    VALUES (data_seq.NEXTVAL, 'ABCD', 'abcd2');

    INSERT INTO data (row_num, name, value) 
    VALUES (data_seq.NEXTVAL, 'PQRS', 'pqrs');

    INSERT INTO data (row_num, name, value) 
    VALUES (data_seq.NEXTVAL, 'PQRS', 'pqrs2');

    INSERT INTO data (row_num, name, value) 
    VALUES (data_seq.NEXTVAL, 'TOTAL', '100');

    INSERT INTO data (row_num, name, value) 
    VALUES (data_seq.NEXTVAL, 'ACCNAME', 'name');

    INSERT INTO data (row_num, name, value) 
    VALUES (data_seq.NEXTVAL, 'BACCEND', NULL);

    INSERT INTO data (row_num, name, value) 
    VALUES (data_seq.NEXTVAL, 'BACCSTART', NULL);

    INSERT INTO data (row_num, name, value) 
    VALUES (data_seq.NEXTVAL, 'ABCD', 'abcd');

    INSERT INTO data (row_num, name, value) 
    VALUES (data_seq.NEXTVAL, 'ABCD', 'abcd2');

    INSERT INTO data (row_num, name, value) 
    VALUES (data_seq.NEXTVAL, 'PQRS', 'pqrs3');

    INSERT INTO data (row_num, name, value) 
    VALUES (data_seq.NEXTVAL, 'PQRS', 'pqrs4');

    INSERT INTO data (row_num, name, value) 
    VALUES (data_seq.NEXTVAL, 'TOTAL', '150');

    INSERT INTO data (row_num, name, value) 
    VALUES (data_seq.NEXTVAL, 'ACCNAME', 'name');

    INSERT INTO data (row_num, name, value) 
    VALUES (data_seq.NEXTVAL, 'BACCEND', NULL);

    INSERT INTO data (row_num, name, value) 
    VALUES (data_seq.NEXTVAL, 'DOCEND', NULL);

  END LOOP;

END;
/

【讨论】:

  • 嗨,本,您的帖子对我的要求非常有用。在这方面需要其他帮助。对于 200 个这样的可重复块,对于 ACCNAME,我必须获取一个将填充到输出 xml 中的值。例如 100abcdabcd2name
  • 嗨 Ben,这里还要提一点,我需要每个块中的任何一个 ,但是这次我注意到我在 xml 中获取了特定块的所有 ABCD 值。跨度>
  • 我已经更新了答案以任意取每个块的第一个 ABCD。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-01
  • 1970-01-01
  • 2018-11-23
  • 2014-01-03
  • 1970-01-01
相关资源
最近更新 更多