SQL Query执行时间太长

时间:2014-11-19 23:34:58

标签: sql oracle

我的下面的查询花了太长时间:

{SELECT /*+ PARALLEL(5) */
    EXTER.ACE_IT_REGION_ID ,
    EXTER.ACE_IT_LOCATION_CODE,
    EXTER.ACE_IT_ORD_NUM,
    EXTER.TOTAL_ITEM_PRICE_PER_ORDER,
    EXTER.TOTAL_ACE_PAYD_AMOUNT,
    EXTER.ACE_ORD_COMPLETION_TIME AS DATEOFDATA
FROM
(SELECT /*+ use_hash(TAB1,TAB2)  +*/
        DISTINCT
        TAB1.ACE_IT_REGION_ID ,
        TAB1.ACE_IT_LOCATION_CODE,
        TAB1.ACE_IT_ORD_NUM,
        TAB1.TOTAL_ITEM_PRICE_PER_ORDER,
        TAB2.TOTAL_ACE_PAYD_AMOUNT,
        TAB1.ACE_ORD_COMPLETION_TIME 
FROM
(SELECT     AAA.ACE_IT_REGION_ID,
            AAA.ACE_IT_LOCATION_CODE,
            AAA.ACE_IT_ORD_NUM,
            AAA.SUM_PRICE_PLUS_TAX,
            AAA.TOTAL_DISCOUNT,
            (AAA.SUM_PRICE_PLUS_TAX-AAA.TOTAL_DISCOUNT) AS TOTAL_ITEM_PRICE_PER_ORDER,
            TRUNC(AAA.ACE_ORD_COMPLETION_TIME)                
    FROM    (SELECT  B.ACE_IT_REGION_ID AS ACE_IT_REGION_ID,
                     B.ACE_IT_LOCATION_CODE AS ACE_IT_LOCATION_CODE,
                     B.ACE_IT_ORD_NUM AS ACE_IT_ORD_NUM, 
                     (SUM(B.ACE_IT_ITEM_PRICE + B.ACE_IT_TAX_AMT)*B.ACE_IT_QTY) AS     SUM_PRICE_PLUS_TAX,    
                     SUM(B.ACE_IT_DISC_AMT+B.ACE_IT_AUTO_DISC_AMT) AS TOTAL_DISCOUNT,
                     TRUNC(A.ACE_ORD_COMPLETION_TIME ) AS ACE_ORD_COMPLETION_TIME
            FROM    POSDB.ACE_ORDERS A, POSDB.ACE_ITEM_TRAN B
            WHERE   A.ACE_ORD_COMPLETION_TIME >= TRUNC(SYSDATE -1) 
            AND     A.ACE_ORD_COMPLETION_TIME < TRUNC(SYSDATE)
            AND     A.ACE_ORD_REGION_ID = B.ACE_IT_REGION_ID
            AND     A.ACE_ORD_LOCATION_CODE = B.ACE_IT_LOCATION_CODE
            AND     A.ACE_ORD_NUM = B.ACE_IT_ORD_NUM
            AND     A.ACE_ORD_TYPE = 'IS'
            AND     (A.ACE_ORD_STATUS = 'CR'
                    OR A.ACE_ORD_STATUS IS NULL
                    OR A.ACE_ORD_STATUS LIKE ' %' 
                    OR  A.ACE_ORD_STATUS LIKE '% ')
            AND     (B.ACE_IT_VOID_IND <> 'V' OR  B.ACE_IT_VOID_IND IS NULL )
            GROUP BY B.ACE_IT_REGION_ID,
                     B.ACE_IT_LOCATION_CODE,
                     B.ACE_IT_ORD_NUM,
                     A.ACE_ORD_COMPLETION_TIME) AAA
    ORDER BY AAA.ACE_IT_REGION_ID,
            AAA.ACE_IT_LOCATION_CODE,
            AAA.ACE_IT_ORD_NUM,   
            AAA.ACE_ORD_COMPLETION_TIME) TAB1,
    (SELECT ACE_PAYD_REGION_ID,
            ACE_PAYD_LOCATION_CODE,
            ACE_PAYD_ORD_NUM,
            SUM(ACE_PAYD_AMOUNT) TOTAL_ACE_PAYD_AMOUNT
     FROM   POSDB.ACE_PAYMENT_DTL
     WHERE  ACE_PAYD_POSTING_TIMESTAMP BETWEEN TRUNC(SYSDATE-1) AND TRUNC(SYSDATE) 
     AND    (ACE_PAYD_STATUS <> 'V' OR   ACE_PAYD_STATUS IS NULL )
     GROUP BY 
            ACE_PAYD_REGION_ID,
            ACE_PAYD_LOCATION_CODE,
            ACE_PAYD_ORD_NUM) TAB2              
WHERE   TAB1.ACE_IT_REGION_ID = TAB2.ACE_PAYD_REGION_ID
AND     TAB1.ACE_IT_LOCATION_CODE = TAB2.ACE_PAYD_LOCATION_CODE
AND     TAB1.ACE_IT_ORD_NUM = TAB2.ACE_PAYD_ORD_NUM
ORDER BY
    TAB1.ACE_IT_REGION_ID ,
    TAB1.ACE_IT_LOCATION_CODE,
    TAB1.ACE_IT_ORD_NUM       ) EXTER        
WHERE   (EXTER.TOTAL_ITEM_PRICE_PER_ORDER = EXTER.TOTAL_ACE_PAYD_AMOUNT)
AND     EXTER.ACE_ORD_COMPLETION_TIME IS NOT NULL;}

请给我任何建议。我已经使用了提示,但仍然不确定为什么花了这么多时间。 所有的桌子都很大,每个都有300,000,000个。

在tab3临时表中加入表格之前需要花费5分钟......但现在即使在20-25分钟之后它也不会返回记录。

3 个答案:

答案 0 :(得分:1)

略微清理以确保每个下一级子集的可读性。还删除了冗余外部查询,并将其添加到内部的WHERE子句中。应该是相同的结果

        SELECT DISTINCT *
           FROM
              ( SELECT
                      AAA.ACE_IT_REGION_ID,
                      AAA.ACE_IT_LOCATION_CODE,
                      AAA.ACE_IT_ORD_NUM,
                      AAA.SUM_PRICE_PLUS_TAX,
                      AAA.TOTAL_DISCOUNT,
                      AAA.SUM_PRICE_PLUS_TAX - AAA.TOTAL_DISCOUNT AS TOTAL_ITEM_PRICE_PER_ORDER,
                      TRUNC(AAA.ACE_ORD_COMPLETION_TIME)  AS DATEOFDATA
                   FROM
                      ( SELECT  
                              B.ACE_IT_REGION_ID,
                              B.ACE_IT_LOCATION_CODE,
                              B.ACE_IT_ORD_NUM,
                              ( SUM( B.ACE_IT_ITEM_PRICE + B.ACE_IT_TAX_AMT)
                                * B.ACE_IT_QTY ) AS  SUM_PRICE_PLUS_TAX,
                              SUM( B.ACE_IT_DISC_AMT + B.ACE_IT_AUTO_DISC_AMT) AS TOTAL_DISCOUNT,
                              TRUNC( A.ACE_ORD_COMPLETION_TIME ) AS ACE_ORD_COMPLETION_TIME
                           FROM
                              POSDB.ACE_ORDERS A, 
                              POSDB.ACE_ITEM_TRAN B
                           WHERE
                                  A.ACE_ORD_TYPE = 'IS'
                              AND A.ACE_ORD_REGION_ID = B.ACE_IT_REGION_ID
                              AND A.ACE_ORD_LOCATION_CODE = B.ACE_IT_LOCATION_CODE
                              AND A.ACE_ORD_NUM = B.ACE_IT_ORD_NUM
                              AND A.ACE_ORD_COMPLETION_TIME >= TRUNC(SYSDATE -1)
                              AND A.ACE_ORD_COMPLETION_TIME < TRUNC(SYSDATE)
                              AND (   A.ACE_ORD_STATUS = 'CR'
                                   OR A.ACE_ORD_STATUS IS NULL
                                   OR A.ACE_ORD_STATUS LIKE ' %' 
                                   OR  A.ACE_ORD_STATUS LIKE '% ')
                              AND (    B.ACE_IT_VOID_IND <> 'V' 
                                   OR  B.ACE_IT_VOID_IND IS NULL )
                           GROUP BY 
                              B.ACE_IT_REGION_ID,
                              B.ACE_IT_LOCATION_CODE,
                              B.ACE_IT_ORD_NUM,
                              A.ACE_ORD_COMPLETION_TIME ) AAA
                   ORDER BY 
                      AAA.ACE_IT_REGION_ID,
                      AAA.ACE_IT_LOCATION_CODE,
                      AAA.ACE_IT_ORD_NUM,
                      AAA.ACE_ORD_COMPLETION_TIME) TAB1,
              ( SELECT
                      APD.ACE_PAYD_REGION_ID,
                      APD.ACE_PAYD_LOCATION_CODE,
                      APD.ACE_PAYD_ORD_NUM,
                      SUM(APD.ACE_PAYD_AMOUNT) TOTAL_ACE_PAYD_AMOUNT
                   FROM
                      POSDB.ACE_PAYMENT_DTL APD
                   WHERE  
                          APD.ACE_PAYD_POSTING_TIMESTAMP 
                            BETWEEN TRUNC(SYSDATE-1) AND TRUNC(SYSDATE)
                      AND (   APD.ACE_PAYD_STATUS <> 'V' 
                           OR APD.ACE_PAYD_STATUS IS NULL )
                   GROUP BY 
                      APD.ACE_PAYD_REGION_ID,
                      APD.ACE_PAYD_LOCATION_CODE,
                      APD.ACE_PAYD_ORD_NUM ) TAB2
           WHERE   
                  TAB1.ACE_IT_REGION_ID = TAB2.ACE_PAYD_REGION_ID
              AND TAB1.ACE_IT_LOCATION_CODE = TAB2.ACE_PAYD_LOCATION_CODE
              AND TAB1.ACE_IT_ORD_NUM = TAB2.ACE_PAYD_ORD_NUM
              AND TAB1.TOTAL_ITEM_PRICE_PER_ORDER = TAB2.TOTAL_ACE_PAYD_AMOUNT,
              AND TAB1.ACE_ORD_COMPLETION_TIME IS NOT NULL
           ORDER BY
              TAB1.ACE_IT_REGION_ID,
              TAB1.ACE_IT_LOCATION_CODE,
              TAB1.ACE_IT_ORD_NUM ) EX

为了帮助优化,我可以推荐以下索引,因为你没有这样的索引 显示在您的结构/索引的帖子中...付款明细索引实际上是一个覆盖索引,意思是具有where,group by和sum单个字段的元素。这样就不必转到原始表页面来进行查询。它可以直接从索引字段中执行,每个字段都应该针对连接进行优化以及在哪里进行批判。

table            index on..
ACE_ORDERS       ( ACE_ORD_TYPE, ACE_ORD_COMPLETION_TIME, ACE_ORD_REGION_ID, ACE_ORD_LOCATION_CODE, ACE_ORD_NUM, ACE_ORD_STATUS )
ACE_ITEM_TRAN    ( ACE_IT_REGION_ID, ACE_IT_LOCATION_CODE, ACE_IT_ORD_NUM, ACE_IT_VOID_IND )

ACE_PAYMENT_DTL  ( ACE_PAYD_POSTING_TIMESTAMP, ACE_PAYD_REGION_ID, ACE_PAYD_LOCATION_CODE, ACE_PAYD_ORD_NUM, ACE_PAYD_STATUS, ACE_PAYD_AMOUNT )

答案 1 :(得分:0)

您需要使用JOINS重写查询,并确保正在连接的字段具有索引。

您修改后的查询应采用以下格式:

SELECT tab1.fld1, tab.fld2
FROM tab1
JOIN tab2 ON (tab2.ID = tab1.ID AND tab2.fld3 = 'xxx')
JOIN tab3 ON (tab3.ID2 = tab2.ID2)
WHERE tab1.fld4 = 'aaa'
and tab3.fld5 > 100

这将允许SQL优化器完成其工作。

答案 2 :(得分:0)

SELECT /*+ PARALLEL(5) */
    EXTER.ACE_IT_REGION_ID ,
    EXTER.ACE_IT_LOCATION_CODE,
    EXTER.ACE_IT_ORD_NUM,
    EXTER.TOTAL_ITEM_PRICE_PER_ORDER,
    EXTER.TOTAL_ACE_PAYD_AMOUNT,
    EXTER.ACE_ORD_COMPLETION_TIME AS DATEOFDATA
FROM
(SELECT /*+ use_hash(TAB1,TAB2)  +*/
        DISTINCT
        TAB1.ACE_IT_REGION_ID ,
        TAB1.ACE_IT_LOCATION_CODE,
        TAB1.ACE_IT_ORD_NUM,
        TAB1.TOTAL_ITEM_PRICE_PER_ORDER,
        TAB2.TOTAL_ACE_PAYD_AMOUNT,
        TAB1.ACE_ORD_COMPLETION_TIME
FROM
(SELECT     AAA.ACE_IT_REGION_ID,
            AAA.ACE_IT_LOCATION_CODE,
            AAA.ACE_IT_ORD_NUM,
            AAA.SUM_PRICE_PLUS_TAX,
            AAA.TOTAL_DISCOUNT,
-- There is no necessary that you write another subquery to finish below sql, you can do it in AAA
            (AAA.SUM_PRICE_PLUS_TAX-AAA.TOTAL_DISCOUNT) AS TOTAL_ITEM_PRICE_PER_ORDER,
            TRUNC(AAA.ACE_ORD_COMPLETION_TIME)
    FROM    (SELECT  B.ACE_IT_REGION_ID AS ACE_IT_REGION_ID,
                     B.ACE_IT_LOCATION_CODE AS ACE_IT_LOCATION_CODE,
                     B.ACE_IT_ORD_NUM AS ACE_IT_ORD_NUM,
                     (SUM(B.ACE_IT_ITEM_PRICE + B.ACE_IT_TAX_AMT)*B.ACE_IT_QTY) AS     SUM_PRICE_PLUS_TAX,
                     SUM(B.ACE_IT_DISC_AMT+B.ACE_IT_AUTO_DISC_AMT) AS TOTAL_DISCOUNT,
                     TRUNC(A.ACE_ORD_COMPLETION_TIME ) AS ACE_ORD_COMPLETION_TIME
            FROM    POSDB.ACE_ORDERS A, POSDB.ACE_ITEM_TRAN B
            WHERE
            A.ACE_ORD_COMPLETION_TIME >= TRUNC(SYSDATE -1)
            AND     A.ACE_ORD_COMPLETION_TIME < TRUNC(SYSDATE)
-- you can remove it replace by trunc(A.ACE_ORD_COMPLETION_TIME) = TRUNC(SYSDATE -1) if there is no index on this column.
--            A.ACE_ORD_COMPLETION_TIME >= TRUNC(SYSDATE -1)
--            AND     A.ACE_ORD_COMPLETION_TIME < TRUNC(SYSDATE)
            AND     A.ACE_ORD_REGION_ID = B.ACE_IT_REGION_ID
            AND     A.ACE_ORD_LOCATION_CODE = B.ACE_IT_LOCATION_CODE
            AND     A.ACE_ORD_NUM = B.ACE_IT_ORD_NUM
            AND     A.ACE_ORD_TYPE = 'IS'
            AND     (A.ACE_ORD_STATUS = 'CR'
                    OR A.ACE_ORD_STATUS IS NULL
-- I think it better to replace blow by OR INSTR(ACE_ORD_STATUS,' ') > -1
--                    OR A.ACE_ORD_STATUS LIKE ' %'
--                    OR  A.ACE_ORD_STATUS LIKE '% '
                    )
            AND     (B.ACE_IT_VOID_IND <> 'V' OR  B.ACE_IT_VOID_IND IS NULL )
            GROUP BY B.ACE_IT_REGION_ID,
                     B.ACE_IT_LOCATION_CODE,
                     B.ACE_IT_ORD_NUM,
                     A.ACE_ORD_COMPLETION_TIME
                     ) AAA
--  remove order by clause in subquery, it's useless.
--    ORDER BY AAA.ACE_IT_REGION_ID,
--            AAA.ACE_IT_LOCATION_CODE,
--            AAA.ACE_IT_ORD_NUM,
--            AAA.ACE_ORD_COMPLETION_TIME
) TAB1,
    (SELECT ACE_PAYD_REGION_ID,
            ACE_PAYD_LOCATION_CODE,
            ACE_PAYD_ORD_NUM,
            SUM(ACE_PAYD_AMOUNT) TOTAL_ACE_PAYD_AMOUNT
     FROM   POSDB.ACE_PAYMENT_DTL
     WHERE  ACE_PAYD_POSTING_TIMESTAMP BETWEEN TRUNC(SYSDATE-1) AND TRUNC(SYSDATE)
     AND    (ACE_PAYD_STATUS <> 'V' OR   ACE_PAYD_STATUS IS NULL )
     GROUP BY
            ACE_PAYD_REGION_ID,
            ACE_PAYD_LOCATION_CODE,
            ACE_PAYD_ORD_NUM) TAB2
WHERE   TAB1.ACE_IT_REGION_ID = TAB2.ACE_PAYD_REGION_ID
AND     TAB1.ACE_IT_LOCATION_CODE = TAB2.ACE_PAYD_LOCATION_CODE
AND     TAB1.ACE_IT_ORD_NUM = TAB2.ACE_PAYD_ORD_NUM
--  remove order by clause in subquery, it's useless.
--ORDER BY
--    TAB1.ACE_IT_REGION_ID ,
--    TAB1.ACE_IT_LOCATION_CODE,
--    TAB1.ACE_IT_ORD_NUM
) EXTER
WHERE   (EXTER.TOTAL_ITEM_PRICE_PER_ORDER = EXTER.TOTAL_ACE_PAYD_AMOUNT)
AND     EXTER.ACE_ORD_COMPLETION_TIME IS NOT NULL;

-- Fix it like this for tempory, please supply explain plan for further.
-- There are so many columns like "ACE_PAYD_STATUS is not null", as you know, index could not contain null value.
-- But I think you can create an similarly index like "create index *** on ACE_PAYMENT_DTL(ACE_PAYD_STATUS,0)" to keep null values in your index.