等于(=)与LIKE的日期数据类型

时间:2013-08-29 07:59:02

标签: sql oracle

首先,我知道这个问题已经普遍发布Equals(=) vs. LIKE。 在这里,我查询ORACLE数据库中的日期类型数据,我发现以下内容,当我以这种方式编写选择语句时:

SELECT ACCOUNT.ACCOUNT_ID, ACCOUNT.LAST_TRANSACTION_DATE
FROM ACCOUNT
WHERE ACCOUNT.LAST_TRANSACTION_DATE LIKE '30-JUL-07';

我得到了我正在寻找的所有行。但是当我使用等于=的符号时:

SELECT ACCOUNT.ACCOUNT_ID, ACCOUNT.LAST_TRANSACTION_DATE
FROM ACCOUNT
WHERE ACCOUNT.LAST_TRANSACTION_DATE = '30-JUL-07';
除了等号,没有什么不同,我什么也得不到。我可以找到任何解释吗?

3 个答案:

答案 0 :(得分:22)

假设LAST_TRANSACTION_DATEDATE列(或TIMESTAMP),那么这两个版本都是非常糟糕的做法。

在这两种情况下,DATE列将根据当前的NLS设置隐式转换为字符文字。这意味着对于不同的客户,您将获得不同的结果。

使用日期文字时 始终 使用to_date()和(!)格式掩码或使用ANSI日期文字。这样你就可以将日期与日期而不是字符串与字符串进因此,对于平等比较,您应该使用:

LAST_TRANSACTION_DATE = to_date('30-JUL-07', 'dd-mon-yy')

请注意,使用“MON”仍然会导致使用不同的NLS设置('DEC''DEZ''MAR''MRZ')的错误。使用月份数字(和四位数年份)更不容易出错:

LAST_TRANSACTION_DATE = to_date('30-07-2007', 'dd-mm-yyyy')

或使用ANSI日期文字

LAST_TRANSACTION_DATE = DATE '2007-07-30'

现在,上述查询很可能不返回任何内容的原因是Oracle DATE列中包含的时间也是如此。上述日期文字隐含地包含时间00:00。如果表格中的时间不同(例如19:54),那么当然日期不相等。

要解决此问题,您可以选择不同的选项:

  1. 在表格列上使用trunc()来“标准化”00:00的时间 trunc(LAST_TRANSACTION_DATE) = DATE '2007-07-30 但是,这将阻止使用LAST_TRANSACTION_DATE
  2. 上定义的索引
  3. 使用between
    LAST_TRANSACTION_DATE between to_date('2007-07-30 00:00:00', 'yyyy-mm-dd hh24:mi:ss') and to_date('2007-07-30 23:59:59', 'yyyy-mm-dd hh24:mi:ss')
  4. 第一个解决方案的性能问题可以通过在trunc(LAST_TRANSACTION_DATE)上创建可以由该表达式使用的索引来解决。但是表达式LAST_TRANSACTION_DATE = '30-JUL-07'也阻止了索引的使用,因为在内部它被处理为to_char(LAST_TRANSACTION_DATE) = '30-JUL-07'

    要记住的重要事项:

    1. 永远不要依赖隐式数据类型转换。 会在某些时候给你带来麻烦。始终比较正确的数据类型
    2. Oracle DATE列始终包含时间,该时间是比较规则的一部分。

答案 1 :(得分:6)

您不应直接将日期与字符串进行比较。你依赖implicit conversions,其规则很难记住。

此外,您选择的日期格式不是最佳的:年份有四位数(Y2K错误?),而且并非所有语言都有一年中第七个月的JUL。您应该使用类似YYYY/MM/DD的内容。

最后,Oracle中的日期是精确到秒的时间点。 所有日期都有时间成分,即使它是00:00:00。当您使用=运算符时,Oracle将比较日期的日期和时间。

这是一个再现你所描述行为的测试案例:

SQL> create table test_date (d date);

Table created

SQL> alter session set nls_date_format = 'DD-MON-RR';

Session altered

SQL> insert into test_date values
  2     (to_date ('2007/07/30 11:50:00', 'yyyy/mm/dd hh24:mi:ss'));

1 row inserted

SQL> select * from test_date where d = '30-JUL-07';

D
-----------

SQL> select * from test_date where d like '30-JUL-07';

D
-----------
30/07/2007

当您使用=运算符时,Oracle会将常量字符串30-JUL-07转换为日期并将该值与列进行比较,如下所示:

SQL> select * from test_date where d = to_date('30-JUL-07', 'DD-MON-RR');

D
-----------

当您使用LIKE运算符时,Oracle会将列转换为字符串并将其与右侧进行比较,这相当于:

SQL> select * from test_date where to_char(d, 'DD-MON-RR') like '30-JUL-07';

D
-----------
30/07/2007

始终将日期与日期和字符串与字符串进行比较。相关问题:

答案 2 :(得分:1)

日期字段不是字符串。在内部,当您使用=时,会对字符串进行隐式转换,因为您的字符串没有所需的精度,所以它与任何内容都不匹配。

我猜测LIKE语句与日期字段的行为有些不同,导致在比较中使用隐式通配符,从而消除了对任何精度的要求。基本上,您的LIKE的工作原理如下:

SELECT ACCOUNT.ACCOUNT_ID, ACCOUNT.LAST_TRANSACTION_DATE
FROM ACCOUNT
WHERE ACCOUNT.LAST_TRANSACTION_DATE BETWEEN DATE('30-JUL-07 00:00:00.00000+00:00') AND DATE('30-JUL-07 23:59:59.99999+00:00');