【问题标题】:PL\SQL XMLTable performancePL\SQL XMLTable 性能
【发布时间】:2016-02-28 05:47:10
【问题描述】:

我有一个解析 XMLTYPE 变量的函数,并且对于每条消息,为 XMLTYPE 变量中的每个标记附加一个具有特定结构的 CLOB。 像这样:

FUNCTION myFunc (px_Header      IN     VARCHAR2,
                                px_Block       IN     XMLTYPE,
                                pn_numLines      OUT PLS_INTEGER)
      RETURN CLOB
   IS
      lcl_return   CLOB := EMPTY_CLOB;
   BEGIN
      pn_numLines := 0;

      FOR item
         IN (          SELECT RPAD (NVL (RECEIPTNUMBER, ' '), 20) AS RECEIPTNUMBER,
                              RPAD (NVL (COMPANYCODE, ' '), 3) AS COMPANYCODE,
                              RPAD (NVL (BRAND, ' '), 3) AS BRAND,
                              RPAD (NVL (POLICYNUMBER, ' '), 20) AS POLICYNUMBER,
                              RPAD (NVL (CLAIMNUMBER, ' '), 20) AS CLAIMNUMBER,
                              RECEIPTAMOUNT
                                 AS receiptAmount
                         FROM XMLTABLE (
                                 'INT_DATA/Item'
                                 PASSING px_Block
                                 COLUMNS RECEIPTNUMBER   VARCHAR2 (20)
                                                            PATH 'RECEIPTNUMBER',
                                         COMPANYCODE     VARCHAR2 (3)
                                                            PATH 'COMPANYCODE',
                                         BRAND           VARCHAR2 (3) PATH 'BRAND',
                                         POLICYNUMBER    VARCHAR2 (20)
                                                            PATH 'POLICYNUMBER',
                                         CLAIMNUMBER     VARCHAR2 (20)
                                                            PATH 'CLAIMNUMBER',
                                         RECEIPTAMOUNT   VARCHAR2 (15)
                                                            PATH 'RECEIPTAMOUNT'))
      LOOP
         lcl_return:=
               lcl_return
            || px_Header
            || 'B2'
            || item.RECEIPTNUMBER
            || item.COMPANYCODE
            || item.BRAND
            || item.POLICYNUMBER
            || item.CLAIMNUMBER
            || item.RECEIPTAMOUNT
            || CHR (13)
            || CHR (10);
         pn_numLines := pn_numLines + 1;
      END LOOP;

      RETURN lcl_return;
   END myFunc ;

如果我有一个小的px_Block,这很有效。但是我有一个案例,我可以有一个很大的 XMLTYPE,这个函数需要很长时间。 我是使用 XMLType 和 XMLTable 的新手。我可以做些什么来提高性能。也许使用 BULK COLLECT 语句?

提前致谢, 菲利普

编辑1: 这是一个仅用于两个实例的 XML 示例。

<INT_DATA>
 <Item>
  <RECEIPTNUMBER>1</RECEIPTNUMBER>
  <COMPANYCODE>148</COMPANYCODE>
  <BRAND>006</BRAND>
  <POLICYNUMBER>72972</POLICYNUMBER>
  <CLAIMNUMBER>2015101504</CLAIMNUMBER>
  <RECEIPTAMOUNT>-10.00</RECEIPTAMOUNT>
 </Item>
 <Item>
  <RECEIPTNUMBER>1</RECEIPTNUMBER>
  <COMPANYCODE>148</COMPANYCODE>
  <BRAND>006</BRAND>
  <POLICYNUMBER>73785</POLICYNUMBER>
  <CLAIMNUMBER>2015101505</CLAIMNUMBER>
  <RECEIPTAMOUNT>-22.50</RECEIPTAMOUNT>
 </Item>
</INT_DATA>

编辑2: 我对我的功能进行了一些更改,改进了 20%。我改变了使用临时 CLOB 附加 CLOB 的方式。 但更好的解决方案会有所帮助。

FUNCTION myFunc (px_Header      IN     VARCHAR2,
                                px_Block       IN     XMLTYPE,
                                pn_numLines      OUT PLS_INTEGER)
      RETURN CLOB
   IS
      lcl_return   CLOB := EMPTY_CLOB;
      v_tmp_clob    CLOB := EMPTY_CLOB;
   BEGIN
      pn_numLines := 0;

      FOR item
         IN (          SELECT RPAD (NVL (RECEIPTNUMBER, ' '), 20) AS RECEIPTNUMBER,
                              RPAD (NVL (COMPANYCODE, ' '), 3) AS COMPANYCODE,
                              RPAD (NVL (BRAND, ' '), 3) AS BRAND,
                              RPAD (NVL (POLICYNUMBER, ' '), 20) AS POLICYNUMBER,
                              RPAD (NVL (CLAIMNUMBER, ' '), 20) AS CLAIMNUMBER,
                              RECEIPTAMOUNT
                                 AS receiptAmount
                         FROM XMLTABLE (
                                 'INT_DATA/Item'
                                 PASSING px_Block
                                 COLUMNS RECEIPTNUMBER   VARCHAR2 (20)
                                                            PATH 'RECEIPTNUMBER',
                                         COMPANYCODE     VARCHAR2 (3)
                                                            PATH 'COMPANYCODE',
                                         BRAND           VARCHAR2 (3) PATH 'BRAND',
                                         POLICYNUMBER    VARCHAR2 (20)
                                                            PATH 'POLICYNUMBER',
                                         CLAIMNUMBER     VARCHAR2 (20)
                                                            PATH 'CLAIMNUMBER',
                                         RECEIPTAMOUNT   VARCHAR2 (15)
                                                            PATH 'RECEIPTAMOUNT'))
      LOOP
         v_tmp_clob :=
           TO_CLOB (px_Header)
        || TO_CLOB ('B2')
        || TO_CLOB (item.RECEIPTNUMBER)
        || TO_CLOB (item.COMPANYCODE)
        || TO_CLOB (item.BRAND)
        || TO_CLOB (item.POLICYNUMBER)
        || TO_CLOB (item.CLAIMNUMBER)
        || TO_CLOB (item.RECEIPTAMOUNT)
        || CHR (13)
        || CHR (10);

     lcl_return := lcl_return || v_tmp_clob;

         pn_numLines := pn_numLines + 1;
      END LOOP;

      RETURN lcl_return;
   END myFunc ;

【问题讨论】:

  • 什么是“大”和“长”?
  • "big" 表示 XML 中主标记的 35k 个实例。 “长时间”是指超过2小时。 :)
  • 好的。那又大又慢!您能否展示一个简单、完整的 XML 示例? pkg_sap_fscd.formataMoedaTXT 函数有什么作用?
  • 是XML处理占用时间还是循环内的CLOB连接?在不执行任何代码的情况下尝试循环。
  • @Rene 你是对的。这是一个 CLOB 附加问题。没有它,需要 48 秒。

标签: oracle plsql xmltype


【解决方案1】:

由于您的 XML 表示一个简单的表结构,您可以使用 dbms_xmlsave 将 XML 数据存储在表中。

首先创建一个匹配 XML 数据的表。

-- Create table
create table TEST
(
  receiptnumber NUMBER,
  companycode   NUMBER,
  brand         VARCHAR2(10),
  policynumber  NUMBER,
  claimnumber   NUMBER,
  receiptamount NUMBER
)

然后使用 dbms_xmlsave 将数据存储在一个表中。

declare
   l_clob   clob;
   l_rows  number;
   l_insctx dbms_xmlsave.ctxtype;
begin
   l_clob := '<INT_DATA>
 <Item>
  <RECEIPTNUMBER>1</RECEIPTNUMBER>
  <COMPANYCODE>148</COMPANYCODE>
  <BRAND>006</BRAND>
  <POLICYNUMBER>72972</POLICYNUMBER>
  <CLAIMNUMBER>2015101504</CLAIMNUMBER>
  <RECEIPTAMOUNT>-10.00</RECEIPTAMOUNT>
 </Item>
 <Item>
  <RECEIPTNUMBER>1</RECEIPTNUMBER>
  <COMPANYCODE>148</COMPANYCODE>
  <BRAND>006</BRAND>
  <POLICYNUMBER>73785</POLICYNUMBER>
  <CLAIMNUMBER>2015101505</CLAIMNUMBER>
  <RECEIPTAMOUNT>-22.50</RECEIPTAMOUNT>
 </Item>
</INT_DATA>';

   l_insctx := dbms_xmlsave.newcontext('TEST');
   dbms_xmlsave.setrowtag(l_insctx, 'Item');
   l_rows := dbms_xmlsave.insertxml(l_insctx, l_clob);
   dbms_xmlsave.closecontext(l_insctx);
end;

也许这表现更好。

【讨论】:

  • 嗨,雷内,谢谢。但我试图不为此使用临时表。
【解决方案2】:

XMLTABLE 的问题在于 Oracle 使用 DOM Parser 来读取 XML。这意味着整个 XML 必须以大量开销加载到内存中。看看DBMS_XMLSTORE。这个包使用SAX Parser,它是基于事件的(或基于流的)。

使用 SAX 解析器,您可以在普通 PC 上读取数千兆字节大小的 XML 文件。

但是,直到 - 比方说 50-100 MB - DOM 解析器应该可以正常工作。

【讨论】:

    【解决方案3】:

    我发现了问题并解决了。 正如@Rene 所说,延迟不在 XML 解析器中,而是在 CLOB 附加中。

    使用“||”将 varchar2 附加到 CLOB 需要很长时间。所以,我需要使用 DBMS_LOB.append。但要做到这一点,我需要在 CLOB 中添加一些东西。我不能追加到一个空的 CLOB。

    这就是我解决这个问题的原因:

    FUNCTION myFunc (px_Header      IN     VARCHAR2,
                                px_Block       IN     XMLTYPE,
                                pn_numLines      OUT PLS_INTEGER)
      RETURN CLOB
    IS
      lcl_return   CLOB;
      v_tmpVarchar   VARCHAR2 (32000);
      lv_line        VARCHAR2 (32000);
    BEGIN
      pn_numLines := 0;
    
      FOR item
         IN (          SELECT RPAD (NVL (RECEIPTNUMBER, ' '), 20) AS RECEIPTNUMBER,
                              RPAD (NVL (COMPANYCODE, ' '), 3) AS COMPANYCODE,
                              RPAD (NVL (BRAND, ' '), 3) AS BRAND,
                              RPAD (NVL (POLICYNUMBER, ' '), 20) AS POLICYNUMBER,
                              RPAD (NVL (CLAIMNUMBER, ' '), 20) AS CLAIMNUMBER,
                              RECEIPTAMOUNT
                                 AS receiptAmount
                         FROM XMLTABLE (
                                 'INT_DATA/Item'
                                 PASSING px_Block
                                 COLUMNS RECEIPTNUMBER   VARCHAR2 (20)
                                                            PATH 'RECEIPTNUMBER',
                                         COMPANYCODE     VARCHAR2 (3)
                                                            PATH 'COMPANYCODE',
                                         BRAND           VARCHAR2 (3) PATH 'BRAND',
                                         POLICYNUMBER    VARCHAR2 (20)
                                                            PATH 'POLICYNUMBER',
                                         CLAIMNUMBER     VARCHAR2 (20)
                                                            PATH 'CLAIMNUMBER',
                                         RECEIPTAMOUNT   VARCHAR2 (15)
                                                            PATH 'RECEIPTAMOUNT'))
      LOOP
         lv_line :=
               px_Header
            || 'B2'
            || item.RECEIPTNUMBER
            || item.COMPANYCODE
            || item.BRAND
            || item.POLICYNUMBER
            || item.CLAIMNUMBER
            || item.RECEIPTAMOUNT
            || CHR (13)
            || CHR (10);
         pn_numLines := pn_numLines + 1;
    
         appendCLOB (lcl_retorno, lv_tmpVarchar, lv_linha);
    
      END LOOP;
    
      RETURN lcl_return;
    END myFunc ;
    

    而 appendCLOB 函数是这样的:

     PROCEDURE appendCLOB (pcl_clob   IN OUT NOCOPY CLOB,
                             pv_vc      IN OUT NOCOPY VARCHAR2,
                             pv_text                  VARCHAR2)
       IS
       BEGIN
          pv_vc := pv_vc || pv_text;
       EXCEPTION
          WHEN VALUE_ERROR
          THEN
             IF pcl_clob IS NULL
             THEN
                -- Add the first varchar
                pcl_clob := pv_vc;
             ELSE
                -- If the clob is not empty, uses the DBMS_LOB.append function
                DBMS_LOB.append (pcl_clob, pv_vc);
                pv_vc := pv_text;
             END IF;
       END;
    

    对于 XML 中的 36k 个实例,我的函数需要不到一分钟的时间。 谢谢大家!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-10
      • 2018-01-29
      • 2012-08-18
      • 2017-08-13
      • 2017-06-09
      • 2019-07-28
      相关资源
      最近更新 更多