为什么周二有时是周日?

时间:2015-10-27 09:16:16

标签: sql oracle date

select to_char(sysdate, 'DAY') from dual;将正确返回TUESDAY

但为什么当我在同一天运行select to_char(to_date(sysdate, 'DD-MON-YYYY'), 'DAY') from dual;时,会返回SUNDAY

由于我尝试运行ORA-01835: day of week conflicts with Julian date时出现select to_date('27-OCT-15', 'D') from dual;错误,我必须在尝试之前将日期(我动态传递给函数)转换为DD-MON-YYYY格式从它获得一周的一天。有谁知道为什么我看到这种不一致?

这里有一个更好的例子(我在示例中替换了列和表名)。

SELECT  DATE, 
    to_char(DATE, 'DAY'), 
    to_char(to_date(DATE, 'DD-MON-YYYY'), 'DAY') 
FROM    DATES
ORDER BY DATE;

结果是;

DATE      TO_CHAR(D TO_CHAR(T
--------- --------- ---------
19-OCT-15 MONDAY    SATURDAY
19-OCT-15 MONDAY    SATURDAY
19-OCT-15 MONDAY    SATURDAY
21-OCT-15 WEDNESDAY MONDAY
21-OCT-15 WEDNESDAY MONDAY
02-NOV-15 MONDAY    SATURDAY
02-NOV-15 MONDAY    SATURDAY

3 个答案:

答案 0 :(得分:10)

  

TO_DATE(SYSDATE

永远不要在 DATE 上应用 TO_DATE 。它迫使Oracle:

  • 首先将其转换为字符串
  • 然后将其转换回日期

基于区域设置特定的NLS设置。您需要 TO_DATE 才能将文字转换为日期。对于日期算术,请保留日期。

SQL> SELECT to_char(to_date('27-OCT-2015', 'DD-MON-YYYY'), 'DAY') FROM dual;

TO_CHAR(T
---------
TUESDAY

ROOT CAUSE

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

Session altered.

SQL> select to_char(to_date(sysdate,'DD-MON-YYYY'),'DD-MON-YYYY') from dual;

TO_CHAR(TO_
-----------
27-OCT-0015

所以年份改为0015。因为,您已将 RR 格式转换为 YYYY ,以获取已经 DATE 的值。

现在,第27天 0015年 SUNDAY

SQL> select to_char(to_date('27-OCT-0015','DD-MON-YYYY'),'DAY') FROM DUAL;

TO_CHAR(T
---------
SUNDAY

要了解内部行为,让我们看一个小型演示:

SQL> SET AUTOT ON EXPLAIN
SQL> SELECT * FROM dual WHERE SYSDATE = TO_DATE(SYSDATE);

no rows selected


Execution Plan
----------------------------------------------------------
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):
---------------------------------------------------

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

你看到了filter(SYSDATE@!=TO_DATE(TO_CHAR(SYSDATE@!)))吗?过滤器发生了什么?

SYSDATE 上的

TO_DATE 在内部被解释为TO_DATE(TO_CHAR(SYSDATE@!))

因此,首先应用 TO_CHAR ,其格式取决于您的本地 NLS_DATE_FORMAT ,然后转换回 DATE 。< / p>

更新根据OP的评论......

使用:

  • TO_DATE 将日期文字(字符串)转换为 DATE 。由于 TO_DATE 与NLS相关,我更愿意在单个对帐单级别明确提及格式和 NLS_DATE_LANGUAGE
SQL> alter session set nls_date_format='YYYY-MM-DD';

Session altered.

SQL> SELECT TO_DATE('27-OCT-2015', 'DD-MON-YYYY','NLS_DATE_LANGUAGE=ENGLISH')
  2  FROM DUAL;

TO_DATE('2
----------
2015-10-27

如果您依赖于客户端的NLS设置,则可能对在某个不同位置使用不同NLS的人不起作用。

SQL> alter session set NLS_DATE_LANGUAGE='FRENCH';

Session altered.

SQL> SELECT TO_DATE('27-OCT-2015', 'DD-MON-YYYY') from dual;
SELECT TO_DATE('27-OCT-2015', 'DD-MON-YYYY') from dual
               *
ERROR at line 1:
ORA-01843: not a valid month
  • TO_CHAR 使用正确的格式模型将DATE转换为所需格式显示字符串

反之亦然。

答案 1 :(得分:5)

由于sysdate已经是此表达式的日期:

to_date(sysdate, 'DD-MON-YYYY')

......既冗余又脆弱。您要求Oracle使用当前具有的NLS_DATE_FORMATNLS_TERRITORYNLS_LANGUAGE值将字符串转换为字符串,然后使用固定(可能不同)的格式将其解析为日期。垃圾进,垃圾出。

一些提示:

  1. 没有充分理由不要将日期字符串化。
  2. 来回转换时,请始终指定格式。
  3. 我也不太喜欢使用月份名称作为交换格式;我认为数字不易出错。

答案 2 :(得分:2)

当您的nls_date formatDD-MON-RR时,转化为:

to_date(sysdate,'dd-mon-yyyy')

将生成字符串27-10-0015(其中sysdate为2015-10-27)。

在15年10月27日是星期天 - 在当时不存在的格里高利历中。