Oracle中日期函数的异常运行时

时间:2010-07-19 14:35:42

标签: sql oracle date-arithmetic

我正在运行一个查询,该查询返回特定日期范围之间的月份对象集合。查询工作正常,但速度非常慢(在本地计算机上约为2秒,在企业开发环境中约为30秒)。这是:

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'时,运行时会缩短到几分之一秒?我认为更大的范围需要更长的时间。这似乎是相反的效果。

4 个答案:

答案 0 :(得分: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_object。

all_objects是一个复杂的视图,因此不会像上面使用的内联视图那样高效。如果您不想使用“connect by”语法,则创建一个整数表并使用它。

答案 1 :(得分:3)

使用MONTHS_BETWEEN()函数消除任意4000个月限制的Janek函数的轻微变体

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') 
; 

答案 2 :(得分:2)

无需使用内联视图,我看到使用了太多日期函数。 如果你跳过这一切,这仍然是:

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.

看起来更容易......

此致 罗布。

答案 3 :(得分: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表,但是如果时间较长,则在缓冲区已满时将返回行。每个查询将在相同的时间内完成运行。