如何在date子句中优化具有to_char的Oracle查询

时间:2010-04-10 18:21:56

标签: performance oracle date

我有一个包含约49403459条记录的表格。

我想查询日期范围内的表格。说04/10/201004/10/2010。但是,日期以格式10-APR-10 10.15.06.000000 AM(时间戳)存储在表中。

当我做的时候

SELECT bunch,of,stuff,create_date
FROM myTable
WHERE TO_CHAR (create_date,'MM/DD/YYYY)' >= '04/10/2010'
AND TO_CHAR (create_date, 'MM/DD/YYYY' <= '04/10/2010'

我在529秒内获得255.59行!这是因为我猜我在每个记录上做TO_CHAR。

但是,当我这样做时

SELECT bunch,of,stuff,create_date
FROM myTable
WHERE create_date >= to_date('04/10/2010','MM/DD/YYYY')
AND create_date <= to_date('04/10/2010','MM/DD/YYYY')

然后我在0秒内得到0.14个结果。

如何快速进行此查询并仍然有效(529)结果?

此时我无法更改索引。现在我认为索引是在create_date列上创建的。

如何转换两个日期范围,以便将第一个日期范围转换为全部为0的时间戳,第二个日期范围转换为时间戳,该时间戳是日期的最后一个时间戳。如果这有道理......?

以下where子句也不会产生任何结果:

WHERE            
create_date >= to_timestamp('04/10/2010 00:00:00.000000','MM/DD/YYYY HH24:MI:SS.FF')
AND
create_date <= to_timestamp('04/10/2010 00:00:00.000000','MM/DD/YYYY HH24:MI:SS.FF')

6 个答案:

答案 0 :(得分:4)

  

我得到529行,但是在255.59秒!   这是因为我想我在做什么   TO_CHAR在每条记录上。

如果你第一次询问generate an execution plan,那么......

explain plan for 
SELECT bunch,of,stuff,create_date
FROM myTable
WHERE TO_CHAR (create_date,'MM/DD/YYYY)' >= '04/10/2010'
AND TO_CHAR (create_date, 'MM/DD/YYYY') <= '04/10/2010'
/

...你会看到它进行全表扫描。那是因为to_char()阻止在CREATE DATE使用索引。

您没有说跑步时返回结果需要多长时间...

SELECT bunch,of,stuff,create_date
FROM myTable
WHERE             
create_date >= to_timestamp('04/10/2010 00:00:00.000000','MM/DD/YYYY HH24:MI:SS.FF') 
AND 
create_date <= to_timestamp('04/10/2010 23:59:59:123000','MM/DD/YYYY HH24:MI:SS.FF')
/

...但我预计它会比4分钟更接近0.14秒。

答案 1 :(得分:3)

当然这不起作用:

WHERE            
    create_date >= to_timestamp('04/10/2010 00:00:00.000000','MM/DD/YYYY HH24:MI:SS.FF')
AND
    create_date <= to_timestamp('04/10/2010 00:00:00.000000','MM/DD/YYYY HH24:MI:SS.FF')

因为那只会返回create_date为4/10/2010 12:00 AM 完全的行!

如果您希望在2010年4月10日的任何时间获取create_date所在的所有行,请使用:

WHERE            
    create_date >= to_timestamp('04/10/2010 00:00:00.000000','MM/DD/YYYY HH24:MI:SS.FF')
AND
    create_date < to_timestamp('04/11/2010 00:00:00.000000','MM/DD/YYYY HH24:MI:SS.FF')

或者如果您愿意:

WHERE create_date BETWEEN to_timestamp('04/10/2010 00:00:00.000000','MM/DD/YYYY HH24:MI:SS.FF')
                      AND to_timestamp('04/10/2010 23:59:59.999999','MM/DD/YYYY HH24:MI:SS.FF')

顺便说一句,当你想表示午夜时,你可以把所有其他部分都留下来。所以你可以说:

WHERE            
    create_date >= to_timestamp('04/10/2010','MM/DD/YYYY')
AND
    create_date < to_timestamp('04/11/2010','MM/DD/YYYY')

答案 2 :(得分:2)

在第一个查询中,您正在进行字符比较而不是日期比较,这不应该产生正确的结果。

例如,使用您的逻辑,01/02/2009将大于01/01/2010,因为当比较时,日组件“02”大于日期组件“01”字符和年份永远不会被评估。

答案 3 :(得分:2)

这有效:

WHERE             
create_date >= to_timestamp('04/10/2010 00:00:00.000000','MM/DD/YYYY HH24:MI:SS.FF') 
AND 
create_date <= to_timestamp('04/10/2010 23:59:59:123000','MM/DD/YYYY HH24:MI:SS.FF') 

答案 4 :(得分:1)

SELECT bunch,of,stuff,create_date
  FROM myTable
 WHERE create_date >= to_date('04/10/2010','MM/DD/YYYY')
   AND create_date < to_date('04/11/2010','MM/DD/YYYY')

日期04/10/2010包含从10日午夜到晚上11:59:59的所有日期值,因此获得低于11日的所有日期将覆盖所有基数。另一种方法是确保myTable中的数据在数据输入时截断了CREATE_DATE字段;我更喜欢DATE字段,如果我关心时间组件,我使用TIMESTAMPs。

答案 5 :(得分:0)

您的第一个查询正在使用错误的结果进行字符串比较。 您的第二个查询需要是:

  

WHERE create_date&gt; = TRUNC(to_date('04 / 10/2010','MM / DD / YYYY'))

或将hh:mi:ss添加到谓词中。这不是简单的工作'因为你要以与Oracle期望的不同的方式格式化日期。