2010-07-19 53 views
3

我正在運行一個查詢,該查詢返回某個日期範圍內的月份的日期對象集合。查詢工作正常,但速度很慢(我的本地計算機約2秒,在我們的公司開發環境約30分鐘)。這是它:Oracle中日期函數的不尋常運行時間

SELECT ADD_MONTHS(TO_DATE('200804', 'YYYYMM'), -1+rownum) AS MONTH 
FROM all_objects 
WHERE ADD_MONTHS(TO_DATE('200804', 'YYYYMM'), -1+rownum) <= TO_DATE('200805', 'YYYYMM') 

目前,它只會返回一個月,但如果您擴展第二個日期字符串,它會返回更多。

我有兩個問題。首先,爲什麼這麼慢?我知道Oracle的功能確實減慢了查詢速度,但在我的工作中,這在開發機器上需要大約30秒。

第二個也是更令人費解的問題:當你將範圍擴展到'201805'時,爲什麼運行時縮短到幾分之一秒?我認爲更大的範圍需要更長的時間。這似乎是相反的效果。

回答

2

沒有必要使用內聯的意見,我可以看到使用了太多日期函數擺脫了任意4000個月限制Janek的功能的輕微變形。 如果跳過了這一切,這仍然是:

SQL> var START_YM varchar2(6) 
SQL> var END_YM varchar2(6) 
SQL> exec :START_YM := '200804'; :END_YM := '201805' 

PL/SQL procedure successfully completed. 

SQL> select add_months(to_date(:START_YM,'yyyymm'),level-1) m 
    2  from dual 
    3 connect by level <= months_between(to_date(:END_YM,'yyyymm'),to_date(:START_YM,'yyyymm'))+1 
    4/

M 
------------------- 
01-04-2008 00:00:00 
01-05-2008 00:00:00 
01-06-2008 00:00:00 
<... 116 rows skipped ...> 
01-03-2018 00:00:00 
01-04-2018 00:00:00 
01-05-2018 00:00:00 

122 rows selected. 

它看起來更容易...

問候, 羅布。

+0

非常好。這也很簡單。 – 2010-07-21 13:37:08

4

使用此相反,

SELECT ADD_MONTHS(TO_DATE('200804', 'YYYYMM'), -1+rn) AS MONTH 
FROM (select level rn from dual connect by level < 4000) 
WHERE ADD_MONTHS(TO_DATE('200804', 'YYYYMM'), -1+rn) <= TO_DATE('200805', 'YYYYMM') 
; 

這避免了ALL_OBJECTS這很可能是你的兩個環境之間的不同。

all_objects是一個複雜的視圖,因此不會像上面使用的內聯視圖一樣有效。如果你不想使用「connect by」語法,那麼創建一個整數表並使用它。

+0

+1爲快速而全面的迴應 – 2010-07-19 14:56:27

+0

感謝您的解釋。你能否回答我的問題#2? – 2010-07-19 15:16:40

+0

@Ishmael對不起,我不能幫你解決第二個問題。也許在SQL Developer中查看查詢計劃將幫助您理解這一點。 – 2010-07-19 15:35:02

3

是被使用MONTHS_BETWEEN()函數

SELECT ADD_MONTHS(TO_DATE('200804', 'YYYYMM'), -1+rn) AS MONTH 
    FROM (select level rn 
      from dual 
      connect by level < abs(months_between(TO_DATE('200804', 'YYYYMM'),TO_DATE('201805', 'YYYYMM')))+2 
     ) 
WHERE ADD_MONTHS(TO_DATE('200804', 'YYYYMM'), -1+rn) <= TO_DATE('201805', 'YYYYMM') 
; 
+0

我喜歡這個答案,我將在我的代碼中使用它。我認爲任意限制會拋出未來的編碼員。這段代碼解釋自己好一點。 – 2010-07-19 15:51:14

0

這裏的一部分困難在於它需要爲ALL_OBJECTS視圖中的每一行評估ADD_MONTHS(TO_DATE('200804', 'YYYYMM'), -1+rownum)。如果您重寫where子句,則它將使用COUNT STOPKEY而不是COUNT的不同計劃。

請嘗試下面的查詢,而不是。我的速度跑得快得多。

SELECT ADD_MONTHS(TO_DATE('200804', 'YYYYMM'), -1+rownum) AS MONTH 
FROM all_objects 
where 
    months_between(date '2008-05-01, date '2008-04-01') >= rownum 

對於使用201805使查詢運行速度更快的評論其實是錯誤的。查詢不會運行得更快,它只會使第一行更快地返回,所以看起來更快。

將結束日期設置爲2008-05-01,它需要在返回任何行之前運行整個ALL_OBJECTS表,然後返回任何行,但具有更長的時間段,當緩衝區已滿時它將返回行。每個查詢將在相同的時間內運行完成。

+0

我不確定我同意你的意見。使用Oracle SQL Developer時,報告的查詢運行時間在使用較大的日期範圍時要短得多,即使我可以看到兩個查詢的所有結果。 – 2010-07-21 13:21:36

+0

剛剛用SQL Developer嘗試過它,看起來你是對的......但花費的時間似乎是將第一行返回而不是所有行的時間。 如果您使用'create table as select'或者將'array fetch size'設置爲200來運行它,這將會帶回所有的行並告訴你它的實際時間.....對於兩者都是一樣的查詢。 – 2010-07-23 11:52:51