【问题标题】:Oracle Timestamp fails at conditionOracle 时间戳在条件下失败
【发布时间】:2017-05-15 05:45:08
【问题描述】:

我有表systemproperties,它存储propertyname (varchar)propertyvalue (clob)。在该属性之一是,

propertyname - RowDate
propertyvalue - 12-MAY-17 04.00.06.883000 PM

还有一张表activity,我用timestamp(6)存储日志数据。

查询以从activity 表中获取日志数据,

select type,startdate from activity where startdate > ?

这里startdatetimestamp(6) 类型。我使用的是实际值,结果什么都没有。但是,如果我使用如下:

select type,startdate from activity 
where startdate > (select to_char(propertyvalue) 
                   from systemproperties where propertyname='RowDate')

我得到的行数大于时间。我无法理解这里的区别。任何人都可以在这里指导我。我可以使用什么解决方案?

这是我的 NLS 设置:

NLS_LANGUAGE    AMERICAN
NLS_TERRITORY   AMERICA
NLS_CURRENCY    $
NLS_ISO_CURRENCY    AMERICA
NLS_NUMERIC_CHARACTERS  .,
NLS_CALENDAR    GREGORIAN
NLS_DATE_FORMAT DD-MON-RR
NLS_DATE_LANGUAGE   AMERICAN
NLS_SORT            BINARY
NLS_TIME_FORMAT     HH.MI.SSXFF AM
NLS_TIMESTAMP_FORMAT    DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT  HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI.SSXFF AM TZR
NLS_DUAL_CURRENCY   $
NLS_COMP    BINARY
NLS_LENGTH_SEMANTICS    BYTE
NLS_NCHAR_CONV_EXCP FALSE

提前致谢。

【问题讨论】:

    标签: sql oracle date timestamp


    【解决方案1】:

    您的属性定义如下:

    propertyvalue - 12-MAY-17 04.00.06.883000 PM
    

    现在您认为这意味着 2017 年 5 月 12 日,但您不是这么说的。因此,当将字符串12-MAY-17 04.00.06.883000 PM 转换为日期时,Oracle 会执行通常的操作,并将世纪默认为00

    演示:

    SQL> alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';
    
    Session altered.
    
    SQL> select sysdate from dual;
    
    SYSDATE
    -------------------
    2017-05-14 23:50:09
    
    SQL> select to_date('17-05-14 00:00:00') my_date from dual;
    
    MY_DATE
    -------------------
    0017-05-14 00:00:00
    
    SQL> 
    

    这就是为什么你会得到你所做的结果。显然你有很多记录在哪里startdate > date '0017-05-12'

    “我可以用什么来解决这个问题?”

    最好的解决方案是正确定义日期。现在是 2017 年,仍然为 Y2K 错误编写代码为时已晚。

    propertyvalue - 12-MAY-2017 04.00.06.883000 PM
    

    或者,您可以在引用属性时使用显式日期掩码:

    startdate > to_date(propertyvalue, 'DD-MON-YY HH.MI.SS.FF6 AM')
    

    在这种情况下,Oracle 将用当前世纪 20 替换掩码的缺失元素。这仍然是一个等待发生的错误,但2000 更有可能成为正确答案。


    "开始日期是一个时间戳(6)"

    没关系,Oracle 在这方面对它们都是一样的:

    SQL> alter table t23 add ts timestamp;
    
    Table altered.
    
    SQL> update t23 set ts = systimestamp;
    
    4 rows updated.
    
    SQL> select count(*) from t23 where ts > to_date('17-05-16 00:00:00');
    
      COUNT(*)
    ----------
             4
    
    SQL> select count(*) from t23 where ts > to_date('17-05-16 00:00:00', 'yy-mm-dd hh24:mi:ss');
    
      COUNT(*)
    ----------
             0
    
    SQL> 
    

    【讨论】:

    • 我在我的机器上也试过了,但是这里的sysdate格式不同,SQL> select sysdate from dual; SYSDATE --------- 15-MAY-17
    • 仅供参考...startdatetimestamp(6)。因此,根据您的评论,oracle 会尝试将其转换为 datetimestamp
    • 我已经修改了我的答案,以说明如何更改显示的日期格式。
    • 这解释得很清楚。再澄清一点,startdate > '12-MAY-2017 04.00.06.883000 PM' 需要 25s 来检索 4 条记录,而 startdate > to_char('12-MAY-2017 04.00.06.883000 PM') 需要 2s
    • 鉴于startdate 是一个时间戳,为什么要将属性转换为字符串而不是日期数据类型?
    【解决方案2】:

    当您比较startdate > your_property_string 时,Oracle 将使用适当的NLS_SESSION 参数作为格式掩码隐式尝试将属性字符串转换为匹配的数据类型(即TIMESTAMP)。因此WHERE 子句将隐式转换为:

    WHERE startdate > TO_TIMESTAMP(
                        your_property_string,
                        ( SELECT value
                          FROM   SYS.NLS_SESSION_PARAMETERS
                          WHERE  parameter = 'NLS_TIMESTAMP_FORMAT' )
                      )
    

    因此,Oracle 可以根据您的 NLS_TIMESTAMP_FORMAT 设置为您的属性字符串提供不同的值。 IE。如果是YYYY-MON-DD HH12:MI:SS.FF6 AM,那么您的日期将被解析为0015-05-17T16:00:06.883000,如果是DD-MON-YY HH12:MI:SS.FF6 AM,那么它将是2017-05-15T16:00:06.883000

    如果您希望在转换字符串时保持一致,则必须在查询中明确指定格式掩码(以及 NLS_DATE_LANGUAGE):

    SELECT type,
           startdate
    FROM   activity 
    WHERE  startdate > TO_TIMESTAMP(
                         ?,
                         'DD-MON-YY HH12.MI.SS.FF6 AM',
                         'NLS_DATE_LANGUAGE=ENGLISH'
                       )
    

    【讨论】:

    • 为了执行这个SELECT value FROM SYS.NLS_SESSION_PARAMETERS WHERE parameter = 'NLS_TIMESTAMP_FORMAT',我得到了这个,DD-MON-RR HH.MI.SSXFF AM
    • @ArockiaRaj 使用隐式转换的最大问题是 NLS 设置是会话参数,任何用户可以在他们自己的会话中更改它们,这会破坏查询没有改变。
    • @ArockiaRaj 传递给原始select type,startdate from activity where startdate > ? 查询的绑定参数的值和数据类型是什么?
    • 值 - 12-MAY-17 04.00.06.883000 PM ,数据类型 - clob 存在于 systemproperties 表中。通过程序,? -> stmt.setString(1, "12-MAY-17 04.00.06.883000 PM")
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-28
    • 1970-01-01
    • 1970-01-01
    • 2017-04-27
    相关资源
    最近更新 更多