3

我正在运行一个查询,该查询返回某个日期范围之间几个月的日期对象集合。查询工作正常,但非常慢(在我的本地机器上约 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

4 回答 4

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”语法,请创建一个整数表并使用它。

于 2010-07-19T14:42:48.283 回答
3

Janek 函数的轻微变体,通过使用 MONTHS_BETWEEN() 函数摆脱了任意 4000 个月的限制

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') 
; 
于 2010-07-19T14:55:40.917 回答
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.

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

问候,罗布。

于 2010-07-20T09:28:28.103 回答
0

这里的部分困难在于它需要评估ADD_MONTHS(TO_DATE('200804', 'YYYYMM'), -1+rownum)ALL_OBJECTS 视图中的每一行。如果重写 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 表,但时间较长,当缓冲区已满时,它将返回行给您。每个查询将在相同的时间内运行完成。

于 2010-07-21T11:37:55.083 回答