0

下の表を見つけてください。データ全体が DOCSTART と DOCEND 内にあります。データは、さらに BACCSTART と BACCEND で囲まれています。このタイプのブロックは繰り返し可能です。BACCSTART と BACCEND 内の各ブロックについて、反復可能な ABCD と TOTAL (ブロックごとに 1 回発生) および ACCNAME (ブロックごとに 1 回発生) のいずれかを選択し、次のような 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

これで私を助けてください。これが時間効率の良いクエリで可能である場合。

オラクル10gです。

要件に変更が加えられました。ACCNAME の値は、約 90 行の別の cofig テーブルに存在します。そこから、ACCNAME の value1 と対応する value2 および value3 という 3 つの値を取得し、xml にタグとして入力する必要があります。そのテーブルに参加すると、パフォーマンスが影響を受けます。提案してください。構成テーブルは次のようになります

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

上に示したデータは、それを long として含むテーブルから取得します。それを 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 のサンプル行) を指定すると、次のコードは約 1 秒で実行され、パフォーマンスの目標を達成するはずです。

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 フラグメントが生成されたら、必要なことは何でも置き換えることができます。

データをテーブルに挿入する場合は、SQL と PL/SQL の間でコンテキストの切り替えがないため高速になるため、 INSERT/SELECTを実行するのではなく、単純に実行SELECT INTOすることをお勧めします。INSERT

この例を設定するために、次の 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 に答える