日期比较返回异常结果 - SQL Oracle

Posted

技术标签:

【中文标题】日期比较返回异常结果 - SQL Oracle【英文标题】:Date comparison returns unusual result - SQL Oracle 【发布时间】:2015-06-16 00:33:13 【问题描述】:

我有一个结构表:

+---------+--------------+-----------------+--- ------------+--------+------+
| week_no | long_week_no | week_start_date |周末结束日期 |月 |年份 |
+---------+-------------+------+------ ---------+--------+------+
| 1 | 1A | 2015 年 1 月 1 日 | 2015 年 3 月 1 日 |一月 | 2015 |
| 1 | 1B | 2015 年 4 月 1 日 | 2015 年 10 月 1 日 |一月 | 2015 |
| 2 | 2 | 2015 年 11 月 1 日 | 17/01/2015 |一月 | 2015 |
| 3 | 3 | 18/01/2015 | 24/01/2015 |一月 | 2015 |
| .. | .. | .. | .. | .. | .. |
| 51 | 51 | 2014 年 12 月 14 日 | 2015 年 12 月 20 日 |十二月 | 2014 |
+---------+-------------+------+------ ---------+-------+------+

当我运行以下语句时:

SELECT * 
FROM   loy_period 
WHERE  To_date('15/04/2015', 'DD/MM/YYYY') BETWEEN 
       To_date(week_start_date, 'DD/MM/YYYY') AND 
       To_date(week_end_date, 'DD/MM/YYYY'); 

SELECT * 
FROM   loy_period 
WHERE  To_date('15/04/2015', 'DD/MM/YYYY') BETWEEN 
       week_start_date AND 
       week_end_date; 

它返回以下内容:

+---------+--------------+-----------------+--- ------------+--------+------+
| week_no | long_week_no | week_start_date |周末结束日期 |月 |年份 |
+---------+-------------+------+------ ---------+--------+------+
| 51 | 51 | 2014 年 12 月 14 日 | 2015 年 12 月 20 日 |十二月 | 2014 |
| 1 | 1A | 2015 年 1 月 1 日 | 2015 年 3 月 1 日 |一月 | 2015 |
+---------+-------------+------+------ ---------+-------+------+

当我使用任何日期运行它时,它会返回正确的期间除了 week_no 51 记录!

我很困惑为什么会这样。两列week_start_dateweek_end_date 的类型为date

Fiddle 按预期工作。

【问题讨论】:

如果它们已经是date 类型,你到底为什么要对它们调用To_Date 永远不要在 DATE 列上使用 TO_DATE。它会将其隐式转换为字符串,然后使用特定于语言环境的 NLS 格式返回日期。 你能把你的样本数据和一个健全的查询版本放入sqlfiddle吗? 您确定第 51 行中的年份正确吗? 您需要处理那个小提琴,直到 a) 显示出您正在寻求帮助的问题,或者 b) 您自己发现了问题。 【参考方案1】:

正确使用 TO_DATE 和 DATE 值对我来说非常适合。

切勿在 DATE 上使用 TO_DATE,它会将其隐式转换为字符串,然后使用特定于语言环境的 NLS 格式返回日期 em>.

'01/01/2015' 不是日期,而是字符串。您必须使用 TO_DATE 将其显式转换为 DATE。

看看会发生什么:

SQL> explain plan for select * from dual where to_date(sysdate) > to_date(sysdate -1);

Explained.

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 3752461848

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |     2 |     2   (0)| 00:00:01 |
|*  1 |  FILTER            |      |       |       |            |          |
|   2 |   TABLE ACCESS FULL| DUAL |     1 |     2 |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
---------------------------------------------------

   1 - filter(TO_DATE(TO_CHAR(SYSDATE@!))>TO_DATE(TO_CHAR(SYSDATE@!-1)))

14 rows selected.

SQL>

所以,实际应用的过滤器是 filter(TO_DATE(TO_CHAR(SYSDATE@!)) 由于基于特定语言环境的 NLS 格式的隐式转换,您将得到不正确的输出。

不管怎样,现在回到你原来的问题。

例如,

假设您的数据如下所示:

设置:

SQL> CREATE TABLE t
  2      (week_no VARCHAR2(2), long_week_no VARCHAR2(2), week_start_date DATE, week_end_date DATE, mnth VARCHAR2(3), yr VARCHAR2(4))
  3  ;

Table created.

SQL>
SQL>
SQL> INSERT ALL
  2      INTO t (week_no, long_week_no, week_start_date, week_end_date, mnth, yr)
  3           VALUES ('1', '1A', TO_DATE('01/01/2015','DD/MM/YYYY'), TO_DATE('03/01/2015','DD/MM/YYYY'), 'JAN', '2015')
  4      INTO t (week_no, long_week_no, week_start_date, week_end_date, mnth, yr)
  5           VALUES ('1', '1B', TO_DATE('04/01/2015','DD/MM/YYYY'), TO_DATE('10/01/2015','DD/MM/YYYY'), 'JAN', '2015')
  6      INTO t (week_no, long_week_no, week_start_date, week_end_date, mnth, yr)
  7           VALUES ('2', '2', TO_DATE('11/01/2015','DD/MM/YYYY'), TO_DATE('17/01/2015','DD/MM/YYYY'), 'JAN', '2015')
  8      INTO t (week_no, long_week_no, week_start_date, week_end_date, mnth, yr)
  9           VALUES ('3', '3', TO_DATE('18/01/2015','DD/MM/YYYY'), TO_DATE('24/01/2015','DD/MM/YYYY'), 'JAN', '2015')
 10      INTO t (week_no, long_week_no, week_start_date, week_end_date, mnth, yr)
 11           VALUES ('51', '51', TO_DATE('20/12/2014','DD/MM/YYYY'), TO_DATE('26/12/2015','DD/MM/YYYY'), 'DEC', '2014')
 12  SELECT * FROM dual
 13  ;

5 rows created.

SQL>
SQL> COMMIT;

Commit complete.

SQL>

表格:

SQL> SELECT * FROM t;

WE LO WEEK_STAR WEEK_END_ MNT YR
-- -- --------- --------- --- ----
1  1A 01-JAN-15 03-JAN-15 JAN 2015
1  1B 04-JAN-15 10-JAN-15 JAN 2015
2  2  11-JAN-15 17-JAN-15 JAN 2015
3  3  18-JAN-15 24-JAN-15 JAN 2015
51 51 20-DEC-14 26-DEC-15 DEC 2014

SQL>

查询以根据 DATE 范围过滤行:

SQL> SELECT *
  2  FROM   t
  3  WHERE  To_date('15/01/2015', 'DD/MM/YYYY') BETWEEN
  4         week_start_date AND
  5         week_end_date;

WE LO WEEK_STAR WEEK_END_ MNT YR
-- -- --------- --------- --- ----
2  2  11-JAN-15 17-JAN-15 JAN 2015
51 51 20-DEC-14 26-DEC-15 DEC 2014

SQL>

【讨论】:

以上是关于日期比较返回异常结果 - SQL Oracle的主要内容,如果未能解决你的问题,请参考以下文章

Oracle两个日期类型字段怎么比较大小

无法正确比较 oracle 中的 2 个日期

ORACLE SQL 查找每个分组的最大日期行

PL/SQL oracle,使用日期和时间的测试窗口

Oracle两个日期类型字段怎么比较大小

oracle sql日期比较