2

发生了一些不寻常的事情。迁移时,我有一些日期超过 2050 年(例如,20.05.2050、21.11、2051),但由于某种原因,Oracle 将它们更改为 1950、1951 等。如果你问我,这很烦人,而且是客户报告的。这些是退休日期,所以显然不能在 1950 年代和 1960 年代

原始表中的日期类似于 YYYYMMDD 格式,即 20500130。所以这是我的合并语句

MERGE INTO employment_data emp                                                     
   USING   temp_02 src
   ON      (TO_NUMBER(src.id) = emp.id)
   WHEN MATCHED THEN UPDATE SET 
       emp.retirement_day = DECODE(TO_NUMBER(src.retirement), 0, NULL, TO_DATE(src.retirement, 'YYYY.MM.DD'));

其他日期都可以(例如 20301204),但 2050 年之后的任何日期都会出错。知道如何解决这个烦恼吗?

提前谢谢:-)

4

2 回答 2

9

问题是DECODE强制进行隐式转换。DECODE函数的返回数据类型由规则确定:

DECODE(expr, search, result [, search, result]* [, default])

在比较之前, Oracle 会自动将expr每个search值转换为第一个值的数据类型search。Oracle 自动将返回值转换为与第一个相同的数据类型result。如果第一个result具有数据类型 CHAR 或者如果第一个result为 null,则 Oracle 将返回值转换为数据类型VARCHAR2

在 OracleNULL中具有默认数据类型,VARCHAR2因此您的DECODE表达式相当于:

DECODE(TO_NUMBER(src.retirement), 0, 
       NULL, 
       TO_CHAR(TO_DATE(src.retirement, 'YYYY.MM.DD')));

如果没有格式,则TO_CHAR使用您的NLS_DATE_FORMAT会话设置,可能是DD-MON-RR丢失世纪信息的默认设置。

正如在这个相关问题中指出的,规则RR如下:

  • 如果指定的两位数年份是 00 到 49,则
    • 如果当前年份的后两位数字是 00 到 49,则返回的年份与当前年份的前两位数字相同。
    • 如果当前年份的后两位是 50 到 99,则返回年份的前 2 位比当前年份的前 2 位大 1。
  • 如果指定的两位数年份是 50 到 99,则
    • 如果当前年份的后两位为 00 到 49,则返回年份的前 2 位比当前年份的前 2 位小 1。
    • 如果当前年份的后两位数字是 50 到 99,则返回的年份与当前年份的前两位数字相同。

这就是为什么 2050 年之前的日期没有触发神秘行为的原因!

于 2013-05-31T15:11:03.543 回答
1

我已经想通了,并认为它可以帮助其他人。

我不确定这是 Oracle 设置的问题还是 PL/SQL Developer 的一些顽皮问题。无论如何,我首先检查了 NLS_DATE_FORMAT。

SELECT value FROM v$nls_parameters
WHERE parameter = 'NLS_DATE_FORMAT'

这表明它设置为 DD-MON-RR。

现在在不更改配置的情况下,我只想更改迁移会话和该会话的设置。

Alter SESSION SET nls_date_format = 'dd-mon-yyyy';

然后运行合并语句。

MERGE INTO employment_data emp                                                     
   USING   temp_02 src
   ON      (TO_NUMBER(src.id) = emp.id)
   WHEN MATCHED THEN UPDATE SET 
       emp.retirement_day = TO_CHAR(TO_DATE(src.retirement,'YYYY.MM.DD'));

这行得通。

于 2013-06-03T07:16:50.333 回答