0

请找到下表。整个数据在 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;

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

4

1 回答 1

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这么多行的 a,性能似乎就会下降(我已经看到一次又一次发生这种情况)。XMLAGG当限制为尽可能少的行时,性能变得更容易接受。

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

我建议您是否将数据插入到表中,然后只需执行INSERT/SELECT而不是SELECT INTO后跟 an 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;
/
于 2013-05-04T14:26:56.037 回答