2017-03-06 58 views
0

我有兩個表都有開始日期和結束日期。這兩個表都與名爲cust_id的列相關聯。我加入這些表來獲取特定的列並按日期範圍限制它應用於一個表上。不管日期範圍是4天還是1小時,我都會看到查詢需要50-55秒。當我提供更小的日期範圍時,我假定Oracle需要解析的行數較少。這是預期的行爲,還是我應該查找一些東西?Oracle - 調整具有日期範圍的查詢

select to_char(t.start_ts,'YYYY-MM-DD HH24:MI'), 
COUNT(CASE WHEN f.fault = 'N' THEN 1 END) success, 
COUNT(CASE WHEN f.fault = 'Y' THEN 1 END) failure 
from customer t,profile f where 1=1 
and t.cust_id = f.cust_id 
and to_char(t.start_ts,'YYYY-MM-DD HH24:MI:SS') between '2017-03-01 00:00:00' 
and '2017-05-01 23:59:59' 
group by to_char(t.start_ts,'YYYY-MM-DD HH24:MI') 
order by to_char(t.start_ts,'YYYY-MM-DD HH24:MI'); 

然在不同的Env對類似表的查詢我在那裏觀察相同的行爲:

Plan hash value: 2851258613 


    --------------------------------------------------------------------------------------------------------------------------- 
    | Id | Operation        | Name    | Rows | Bytes | Cost (%CPU)| Time  | Pstart| Pstop | 
    --------------------------------------------------------------------------------------------------------------------------- 
    | 0 | SELECT STATEMENT      |     |  2 | 362 | 11651 (1)| 00:00:01 |  |  | 
    | 1 | SORT GROUP BY      |     |  2 | 362 | 11651 (1)| 00:00:01 |  |  | 
    | 2 | NESTED LOOPS      |     |  2 | 362 | 11650 (1)| 00:00:01 |  |  | 
    | 3 | NESTED LOOPS      |     |  2 | 362 | 11650 (1)| 00:00:01 |  |  | 
    | 4 |  PARTITION RANGE ALL    |     |  2 | 284 | 11644 (1)| 00:00:01 |  1 | 41 | 
    |* 5 |  TABLE ACCESS BY LOCAL INDEX ROWID| TXNS    |  2 | 284 | 11644 (1)| 00:00:01 |  1 | 41 | 
    |* 6 |  INDEX SKIP SCAN     | XIE1TXNS   |  4 |  | 11641 (1)| 00:00:01 |  1 | 41 | 
    |* 7 |  INDEX RANGE SCAN     | XAK1FRONTEND_DTLS |  1 |  |  2 (0)| 00:00:01 |  |  | 
    | 8 | TABLE ACCESS BY GLOBAL INDEX ROWID | FRONTEND_DTLS  |  1 | 39 |  3 (0)| 00:00:01 | ROWID | ROWID | 
    --------------------------------------------------------------------------------------------------------------------------- 

    Predicate Information (identified by operation id): 
    --------------------------------------------------- 

     5 - filter("T"."SRVC_NAME"='ControllerSvc' AND "T"."SRVC_VERSION"='10.00' AND 
        "T"."SRC_SERV_ID"<>'test' AND "T"."SRC_SERV_ID"<>'endtoendtesting' AND "T"."SRVR_NODE_NAME" NOT LIKE 
        '%test.net' AND "T"."SRC_SERV_ID"<>'test' AND "T"."SRC_SERV_ID"<>'SYN') 
     6 - access("T"."SRVC_OP_NAME"='getTestInfo') 
      filter("T"."SRVC_OP_NAME"='getTestInfo' AND TO_CHAR(INTERNAL_FUNCTION("T"."START_TS"),'YYYY-MM-DD 
        HH24:MI:SS')>='2017-03-01 00:00:00' AND TO_CHAR(INTERNAL_FUNCTION("T"."START_TS"),'YYYY-MM-DD 
        HH24:MI:SS')<='2017-05-01 23:59:59') 
     7 - access("T"."TXN_ID"="F"."TXN_ID") 

PS: 我無法查找解釋計劃,因爲我沒有足夠的訪問。

+0

沒有獲得基本的診斷工具,如解釋計劃或能力SQL監視器,你所有的答案將是猜測。我鼓勵你訪問這些工具,以便我們更好地幫助你。 – BobC

+0

Hi @BobC。我從一個不同的env開始瞭解釋計劃,我在那裏看到類似的行爲。 –

回答

2

目前還不清楚您是否有start_ts的索引,但您期望更短的時間跨度應該返回更快的結果你可能有。如果你不這樣做,你可能需要添加一個。在那裏有索引,你查詢的方式會阻止它被使用。你正在做的:

and to_char(t.start_ts,'YYYY-MM-DD HH24:MI:SS') between '2017-03-01 00:00:00' 

和「2017年5月1日23:59:59」

這意味着什麼每一行(即其他謂詞匹配)都必須有start_ts值轉換爲字符串,然後你將該字符串與另外兩個固定字符串進行比較。雖然這會起作用,但速度很慢。您可以從解釋計劃中看到該列正在filter部分中檢查,而不是access部分。 (即使沒有索引,它仍然是額外的開銷;使用索引(或大多數函數調用)將阻止索引被使用)。

您應該比較正確的數據類型,有無索引,但特別是索引。如果列數據類型是DATE那麼你可以做:

and t.start_ts between to_date('2017-03-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS') 
    and to_date('2017-05-01 23:59:59', 'YYYY-MM-DD HH24:MI:SS') 

,或者如果它是一個TIMESTAMP(顧名思義),你可以這樣做:

and t.start_ts between to_timestamp('2017-03-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS') 
    and to_timestamp('2017-05-01 23:59:59', 'YYYY-MM-DD HH24:MI:SS') 

,但將跳過帶有小數秒任何時間 - 例如23:59:59.543 - 因此可能導致錯誤的結果。它的安全做到:

and t.start_ts >= timestamp '2017-03-01 00:00:00' 
and t.start_ts < timestamp '2017-05-02 00:00:00' 

...這裏我也切換到timestamp literals使其更短,但它與使用to_timestamp()與8種格式掩碼。

Oracle可能仍然決定不使用索引(如果存在);或者可能會繼續首先使用分區修剪(或者改爲)。它取決於所使用的所有謂詞的數據和選擇性,優化器選擇最佳方法。使用正確的數據類型和而不是阻止任何可用的索引提供給它一個更好的選擇最佳計劃的機會。

+0

非常感謝@Alex Poole。 –

+0

我有一個與此組相似的組,由 - to_char(t.start_ts,'YYYY-MM-DD')。這是好的還是會有任何開銷,因爲我正在轉換爲角色? –

+1

@PunterVicky - 您正在轉換爲選擇列表中的字符串,因此在group-by中執行該操作並不會增加任何實際開銷,不會。這隻會發生在'where'子句選擇的行上,所以(希望)會比以前小得多 - 並且不會影響索引使用。 (如果你所有的值都沒有小數秒*你可以*通過t.start_ts來分組,但是如果*有*分數秒將會改變結果,並且更清楚地得到選擇列表,匹配 - 避免任何關於你在做什麼的混淆。) –

3

嘗試修改此:

and to_char(t.start_ts,'YYYY-MM-DD HH24:MI:SS') between '2017-03-01 00:00:00' 
and '2017-05-01 23:59:59' 

這樣:

and t.start_ts between to_date('2017-03-01 00:00:00','YYYY-MM-DD HH24:MI:SS') 
    and to_date('2017-05-01 23:59:59','YYYY-MM-DD HH24:MI:SS') 

調用上一列功能可以防止指數被正確使用。這假定你有一個關於start_ts的索引。或者(但我會建議第一個選項),是創建一個基於功能的索引 - https://oracle-base.com/articles/8i/function-based-indexes

+0

非常感謝@OldProgrammer! –

0

爲了看到運行時統計,看看你的基數估計是準確的,你應該這樣做:

alter session set timed_statistics=ALL; 

select * from table(dbms_xplan.display_cursor(null, null, 'ALLSTATS LAST'));