在oracle中具有范围的可重复块

时间:2013-05-04 12:28:55

标签: xml oracle hierarchy aggregation

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

这样的xml
<BACCSTART>
<TOTAL>100</TOTAL>
<ABCD>abcd</ABCD>
<ACCNAME>name</ACCNAME>
</BACCSTART>
每个这样的块

。目前我正在使用for循环,但性能达不到标准。它将有大约200个这样的块,我必须在15秒内形成xmls。目前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行的cofig表中。从那里我必须拿起三个值,例如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;

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

1 个答案:

答案 0 :(得分:0)

鉴于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; 

我试图让它在一个查询中完成所有工作,但是XMLAGG性能似乎只要你应用GROUP BY这么多行(我已经是看到一次又一次地发生)。当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;
/