2015-07-10 70 views
0

之后直接调用过程相比花费很多时间我有一个INSERT触发器调用我的过程来处理STAGE_TBL的XMLTYPE字段中的XML并将数据插入到PROCESSED_DATA_TBL中 为了根据处理XML的结果更新STAGE_TBL行上的状态,我必须使用INSERT触发器之前(我也可以使用Compound Trigger,但我没有尝试过)。INSERT触发器之前的性能问题,与插入

我遇到的问题是我的XML是巨大的它可以有大约100 - 2000 rp_sendRow块,如果是巨大的,则触发正在采取这么多的时间。我尝试了100 rp_sendRow,大约需要4分钟才能触发。 但是,如果我禁用触发器并插入STAGE_TBL,然后使用该ID为新插入的记录调用XML_PROCESS,那么它将在不到一秒的时间内从SQL Developer完成(处理XML并插入PROCESSED_DATA_TBL)。

我无法使用常规SQL从SQL Developer中插入巨大的XML,因为有4000个字符限制,因为数据库不在我的本地,我甚至不能使用XMLType(bfilename('XMLDIR','MY.xml' )选项,这样我使用JDBC代码中插入大量XML。

我呼吁直接从JDBC的XML_PROCESS为相同的XML,它比第二耗时还不到处理和插入PROCESSED_DATA_TBL

请让我知道为什么触发需要时间?

我使用的是Oracle 11g,SQL开发4.1.0.19

--Trigger Code 
create or replace TRIGGER STAGE_TRIGGER 
BEFORE INSERT ON STAGE_TBL 
FOR EACH ROW 
DECLARE 
ROW_COUNT NUMBER; 
PROCESS_STATUS VARCHAR2(1); 
STATUS_DESCRIPTION VARCHAR2(300); 

    BEGIN 
    XML_PROCESS(:NEW.ID, :NEW.XML_DOCUMENT, PROCESS_STATUS, STATUS_DESCRIPTION, ROW_COUNT); 
    IF(ROW_COUNT > 0) THEN 
     :NEW.STATUS    := PROCESS_STATUS; 
     :NEW.STATUS_DATE  := SYSDATE; 
     :NEW.STATUS_DESCRIPTION := STATUS_DESCRIPTION; 
     :NEW.SHRED_TS   := SYSTIMESTAMP; 
    ELSE--This is to handle 0 records inserted scenario & exception scenarios 
     :NEW.STATUS    := STATUS.ERROR; 
     :NEW.STATUS_DATE  := SYSDATE; 
     :NEW.STATUS_DESCRIPTION := STATUS_DESCRIPTION; 
    END IF;  
    EXCEPTION 
     WHEN OTHERS THEN 
     :NEW.STATUS    := PROCESS_STATUS; 
     :NEW.STATUS_DESCRIPTION := STATUS_DESCRIPTION; 
     NULL; 
    END STAGE_TRIGGER; 

--Stored Procedure 
create or replace PROCEDURE XML_PROCESS (ID IN RAW, xData IN XMLTYPE, PROCESS_STATUS OUT VARCHAR2, STATUS_DESCRIPTION OUT VARCHAR2, ROW_COUNT OUT NUMBER) AS 
BEGIN 
    INSERT ALL INTO PROCESSED_DATA_TBL 
     (ID, 
     STORE, 
     SALES_NBR, 
     UNIT_COST, 
     ST_FLAG, 
     ST_DATE, 
     ST, 
     START_QTY, 
     START_VALUE, 
     START_ON_ORDER, 
     HAND, 
     ORDER, 
     COMMITED, 
     SALES, 
     RECEIVE, 
     VALUE, 
     COST, 
     ID_1, 
     ID_2, 
     ID_3, 
     UNIT_PRICE, 
     EFFECTIVE_DATE, 
     STATUS, 
     STATUS_DATE, 
     STATUS_REASON) 
     VALUES (ID 
       ,storenbr 
       ,SalesNo 
       ,UnitCost  
       ,StWac 
       ,StDt   
       ,St   
       ,StartQty   
       ,StartValue  
       ,StartOnOrder  
       ,Hand  
       ,Order 
       ,Commit  
       ,Sales  
       ,Rec  
       ,Value 
       ,Id1   
       ,Id2   
       ,Id3   
       ,UnitPrice 
       ,to_Date(EffectiveDate||' '||EffectiveTime, 'YYYY-MM-DD HH24:MI:SS') 
       ,'N'  
       ,SYSDATE 
       ,'XML PROCESS INSERT')  
     SELECT E.* FROM XMLTABLE('rp_send/rp_sendRow' PASSING xData COLUMNS 
           store   VARCHAR(20) PATH 'store' 
           ,SalesNo   VARCHAR(20) PATH 'sales' 
           ,UnitCost   NUMBER  PATH 'cost' 
           ,StWac   VARCHAR(20) PATH 'flag' 
           ,StDt    DATE  PATH 'st-dt' 
           ,St    NUMBER  PATH 'st' 
           ,StartQty   NUMBER  PATH 'qty' 
           ,StartValue  NUMBER  PATH 'value' 
           ,StartOnOrder  NUMBER  PATH 'order' 
           ,Hand    NUMBER  PATH 'hand' 
           ,Order   NUMBER  PATH 'order' 
           ,Commit   NUMBER  PATH 'commit' 
           ,Sales   NUMBER  PATH 'sales' 
           ,Rec    NUMBER  PATH 'rec' 
           ,Value   NUMBER  PATH 'val' 
           ,Id1    VARCHAR(30) PATH 'id-1' 
           ,Id2    VARCHAR(30) PATH 'id-2' 
           ,Id3    VARCHAR(30) PATH 'id-3' 
           ,UnitPrice  NUMBER  PATH 'unit-pr' 
           ,EffectiveDate VARCHAR(30) PATH 'eff-dt' 
           ,EffectiveTime VARCHAR(30) PATH 'eff-tm' 
     ) E;  
     ROW_COUNT   := SQL%ROWCOUNT; 
     PROCESS_STATUS  := STATUS.PROCESSED; 
     STATUS_DESCRIPTION := ROW_COUNT || ' Rows Successfully Inserted '; 
     EXCEPTION 
     WHEN DUP_VAL_ON_INDEX THEN 
     BEGIN 
     ROW_COUNT   := 0; 
     PROCESS_STATUS  := STATUS.ERROR; 
     STATUS_DESCRIPTION := SUBSTR(SQLERRM, 1, 250); 
     END; 
     WHEN OTHERS THEN 
     BEGIN 
     ROW_COUNT   := 0; 
     PROCESS_STATUS  := STATUS.ERROR; 
     STATUS_DESCRIPTION := SUBSTR(SQLERRM, 1, 250); 
     END; 
END XML_PROCESS; 
--Standalone Procedure calling XML_PROCESS 
SET DEFINE OFF 
DECLARE 
ROW_COUNT NUMBER; 
PROCESS_STATUS VARCHAR2(1); 
STATUS_DESCRIPTION VARCHAR2(300); 
V_ID NUMBER; 
V_XML XMLTYPE; 
BEGIN 
    SELECT ID, XML_DOCUMENT INTO V_ID, V_XML FROM STAGE_TBL WHERE ID = '7954'; 
    XML_PROCESS(ID, V_XML, PROCESS_STATUS, STATUS_DESCRIPTION, ROW_COUNT); 

    update STAGE_TBL SET STATUS = PROCESS_STATUS, 
            STATUS_DATE = SYSDATE, 
            STATUS_DESCRIPTION = STATUS_DESCRIPTION 
          WHERE ID = V_ID; 
END; 


XML 
<?xml version = \"1.0\" encoding = \"UTF-8\"?> 
<rp_send xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"> 
    <rp_sendRow> 
     <store>0123</store> 
     <sales>022399190</sales> 
     <cost>0.01</cost> 
     <flag>true</flag> 
     <st-dt>2013-04-19</st-dt> 
     <st>146.51</st> 
     <qty>13.0</qty> 
     <value>0.0</value> 
     <order>0.0</order> 
     <hand>0.0</hand> 
     <order>0.0</order> 
     <commit>0.0</commit> 
     <sales>0.0</sales> 
     <rec>0.0</rec> 
     <val>0.0</val> 
     <id-1/> 
     <id-2/> 
     <id-3/> 
     <unit-pr>13.0</unit-pr> 
     <eff-dt>2015-06-16</eff-dt> 
     <eff-tm>09:12:21</eff-tm> 
    </rp_sendRow> 
</rp_send> 

回答

0

有两种许多未知的变数,以确定问题,但这个信息,我见有四(编辑包括更多的答案)可能的答案:

1)如果插入许多行只有一个声明(INSERT ... SELECT)触发器会降低性能。

但是,您的独立程序调用示例仅使用一行(ID = '7954'),因此我认为问题只存在一次元组插入。在这种情况下1)不是问题。

2)你有STAGE_TBL(XML_DOCUMENT)某种指标。当调用BEFORE INSERT触发器时,XMLType未编入索引,并且触发器将调用具有XML_DOCUMENT的非索引版本的过程。但是在独立程序示例中,插入并索引XML_DOCUMENT,因此过程使用该索引。

上复杂对象复杂的索引可以通过Oracle优化不仅从一个表中选择数据时被使用,但是它们可以在处理数据本身时使用。这意味着:如果您有特定数据的索引,则可以由使用此数据的过程使用。而Oracle的XMLType是复杂的对象,可以通过多种方式进行索引(请参阅:http://docs.oracle.com/cd/B28359_01/appdev.111/b28369/xdb_indexing.htm#CHDCGACG)。

我认为,当XML_DOCUMENT在STAGE_TBL实际插入XMLTABLE函数进行优化。

您可以致电独立的程序与STAGE_TBL未提取的XML_DOCUMENT(或任何表,可以索引文件)进行测试。在这种情况下,触发和独立,表演应该是相似的。

EDITED:您评论说您已经测试了第二个答案,并且性能问题仍然存在。所以我包含第三个选项:

3)您在STAGE_TBL中包含了XML验证检查约束。而这种验证是性能差异的根源。独立的示例不验证XML文档,但插入验证它。

您可以通过禁用触发器来检查是否发生了这种情况。如果没有触发器的插入仍然很慢,那么问题不是触发器,而是XML验证。

EDITED:您评论说您已测试第三个答案,并且性能问题仍然存在。所以我包括第四个选项:

4)在(https://community.oracle.com/thread/2526907)中,处理大XML文档时描述了XMLTable的性能问题。他们评论说,在这些情况下使用TABLE(XMLSequence())方法会更好,因为XMLTable会创建大的中间结果,而TABLE(XMLSequence())则不会。

所以在你INSERT语句更改您的SELECT来自:

SELECT E.* FROM XMLTABLE('rp_send/rp_sendRow' PASSING xData COLUMNS 
          store   VARCHAR(20) PATH 'store' 
          ,SalesNo   VARCHAR(20) PATH 'sales' 
          ,UnitCost   NUMBER  PATH 'cost' 
          ,StWac   VARCHAR(20) PATH 'flag' 
          ,StDt    DATE  PATH 'st-dt' 
          ... 
          ... 
          ,EffectiveTime VARCHAR(30) PATH 'eff-tm' 
    ) E;  

要:

SELECT value(e).extract('//store/text()').getStringVal() store, 
     value(e).extract('//sales/text()').getStringVal() SalesNo, 
     value(e).extract('//cost/text()').getNumberVal() UnitCost, 
     value(e).extract('//flag/text()').getStringVal() StWac, 
     to_date(value(e).extract('//st-dt/text()').getStringVal(),'YYYY-MM-DD') StDt, 
     ... 
     ... 
     value(e).extract('//eff-tm/text()').getStringVal() EffectiveTime 
    FROM TABLE(XMLSEQUENCE(EXTRACT(xData, '/rp_send/rp_sendRow'))) e; 
+0

我已经测试了独立的程序来处理XML文件,它不是来自任何表和被处决非常类似于从STAGE_TBL中获取的那个。我已经使用JDBC直接调用存储过程,以便将大量的XML作为输入发送到过程。 –

+0

@ java-ocean我已经包含了第三个可能的答案。请测试它。 – acesargl

+0

感谢您的回复,我的STAGE_TBL没有XML验证约束。我已经使用禁用触发器进行了测试,插入速度非常快。 –