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

Haw*_*awk 14 sql oracle

首先,我知道这个问题一般都是等于(=)和LIKE.在这里,我在ORACLE数据库上查询日期类型数据,我发现以下内容,当我以这种方式编写select statment时:

SELECT ACCOUNT.ACCOUNT_ID, ACCOUNT.LAST_TRANSACTION_DATE
FROM ACCOUNT
WHERE ACCOUNT.LAST_TRANSACTION_DATE LIKE '30-JUL-07';
Run Code Online (Sandbox Code Playgroud)

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

SELECT ACCOUNT.ACCOUNT_ID, ACCOUNT.LAST_TRANSACTION_DATE
FROM ACCOUNT
WHERE ACCOUNT.LAST_TRANSACTION_DATE = '30-JUL-07';
Run Code Online (Sandbox Code Playgroud)

即使除了等号之外什么都没有什么,我什么也得不到.我可以找到任何解释吗?

a_h*_*ame 21

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

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

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

LAST_TRANSACTION_DATE = to_date('30-JUL-07', 'dd-mon-yy')
Run Code Online (Sandbox Code Playgroud)

请注意,使用"MON"仍然可能导致使用不同NLS设置('DEC'相对'DEZ''MAR'相对'MRZ')的错误.使用月份数(和四位数年份)更不容易出错:

LAST_TRANSACTION_DATE = to_date('30-07-2007', 'dd-mm-yyyy')
Run Code Online (Sandbox Code Playgroud)

或使用ANSI日期文字

LAST_TRANSACTION_DATE = DATE '2007-07-30'
Run Code Online (Sandbox Code Playgroud)

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

要解决此问题,您有不同的选择:

  1. 使用trunc()表列来"规范化"时间,00:00 trunc(LAST_TRANSACTION_DATE) = DATE '2007-07-30 但是会阻止使用定义的索引LAST_TRANSACTION_DATE
  2. 使用 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')

可以通过创建trunc(LAST_TRANSACTION_DATE)该表达式可以使用的索引来解决第一个解决方案的性能问题.但是该表达式LAST_TRANSACTION_DATE = '30-JUL-07'也会阻止索引的使用,因为它在内部被处理为to_char(LAST_TRANSACTION_DATE) = '30-JUL-07'

要记住的重要事项:

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


Vin*_*rat 7

您不应该直接将日期与字符串进行比较.你依赖隐式转换,其规则很难记住.

此外,您选择的日期格式不是最佳的:年份有四位数(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
Run Code Online (Sandbox Code Playgroud)

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

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

D
-----------
Run Code Online (Sandbox Code Playgroud)

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

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

D
-----------
30/07/2007
Run Code Online (Sandbox Code Playgroud)

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

  • 你必须使用带有两个参数的`to_date`(常量**和**格式:).同样`LIKE`严格来说是一个字符串运算符,因此不适用于日期. (4认同)