准备好的语句:比较时间戳和分钟的粒度

Prepared statement: Compare timestamp with granularity of minutes

我用 SQL 语句创建了一个 PreparedStatement,如下所示:

SELECT *
FROM MY_TABLE
WHERE MY_DATE = ?

我将 MY_DATE 值设置为

preparedStatement.setTimestamp(1, myDate);

这样数据库会检查整个时间戳是否相等。

现在我想将“刚刚”的粒度减少到几分钟。我怎样才能做到这一点?

如果你使用TRUNC()函数,那么你可以忽略值的粒度,低于某个级别。

所以,在你的情况下:

SELECT *
FROM MY_TABLE
WHERE TRUNC(MY_DATE, 'MI') = TRUNC(?, 'MI')

可以使用其他粒度:有关详细信息,请参阅 Oracle's Documentation(默认为在相关日期截断为 00:00:00)。

当然,如果您存储的时间戳已被截断为分钟,那么您可以省略左侧的 TRUNC 调用,并利用索引(如果该列上有索引)。

使用TRUNC 会抑制日期列 上的任何常规索引。提高性能的一种方法是创建一个 基于函数的索引

但是,更好的方法是使用显式范围条件

例如,

SELECT *
FROM MY_TABLE
WHERE
  MY_DATE >= TO_DATE('24-MAR-2015','DD-MON-YYYY')
  AND MY_DATE < TO_DATE('24-MAR-2015','DD-MON-YYYY')+1;

或者,

使用BETWEEN(无论如何都会被优化器重写为上述范围条件):

SELECT *
FROM MY_TABLE
WHERE
  MY_DATE BETWEEN TO_DATE('24-MAR-2015','DD-MON-YYYY')
  AND TO_DATE('24-MAR-2015','DD-MON-YYYY') + (1-1/24/60/60);

更多信息

有了范围条件,优化器实际上按以下方式重写了查询:

access("MY_DATE">=TO_DATE(' 2015-03-24 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
              "MY_DATE"<TO_DATE(' 2015-03-24 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))

并且将有一个 INDEX RANGE SCAN 操作而不是 TABLE ACCESS FULL。如果使用 TRUNC,没有基于函数的索引,这就是优化器应用过滤谓词的方式:

filter(TRUNC(INTERNAL_FUNCTION("MY_DATE"))=TO_DATE(' 2015-03-24 00:00:00',
              'syyyy-mm-dd hh24:mi:ss'))

一篇关于 Impact of the TRUNC Function on an Indexed Date Column

的好文章