【问题标题】:Prepared statement: Compare timestamp with granularity of minutes准备好的语句:将时间戳与分钟粒度进行比较
【发布时间】:2015-03-25 15:38:22
【问题描述】:

我有一个PreparedStatement 使用这样的 SQL 语句创建:

SELECT *
FROM MY_TABLE
WHERE MY_DATE = ?

我将MY_DATE 值设置为

preparedStatement.setTimestamp(1, myDate);

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

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

【问题讨论】:

  • 从性能的角度来看,使用明确的范围条件。看我的回答。

标签: java oracle jdbc timestamp prepared-statement


【解决方案1】:

使用 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的好文章

【讨论】:

    【解决方案2】:

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

    所以,在你的情况下:

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

    其他粒度可用:详见Oracle's Documentation(默认截断为相关日期的00:00:00)。

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

    【讨论】:

    • 您不想在 MY_DATE 调用 TRUNC,因为它可能会使索引的使用无效。
    • 如果您有 indexed 列,则使用 TRUNC 不是一个好主意。最好使用范围条件。
    猜你喜欢
    • 2018-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-18
    • 1970-01-01
    • 2017-07-02
    相关资源
    最近更新 更多