2015-07-10 67 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驗證約束。我已經使用禁用觸發器進行了測試,插入速度非常快。 –