String to Timestamp 如何在 oracle where 子句中没有显式转换的情况下工作

How String to Timestamp works without explicit conversion in oracle where clause

以下两个SQL都在我的Oracle数据库中工作,结果或结果计数完全相同且正确。 CREATE_TIME 是时间戳列。

select * from EMPLOYEE where CREATE_TIME =(or >) '2022-01-21 2:49:38.251'
select * from EMPLOYEE where CREATE_TIME =(or >) '22-01-21 2:49:38.251000'

很好奇它是如何工作的,因为字符串格式不同,我没有使用 TO_* 转换函数,Oracle NLS(作为默认转换规则)显示完全不同的格式。

PARAMETER -> VALUE
NLS_TIMESTAMP_TZ_FORMAT -> DD-MON-RR HH.MI.SSXFF AM TZR
NLS_TIME_TZ_FORMAT -> HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_FORMAT -> DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_FORMAT -> HH.MI.SSXFF AM

查了资料没找到答案,如果有人能回答我并提供一个information/documentlink供参考,将不胜感激。

如果您尝试使用 CAST(value AS TIMESTAMP) 执行显式转换或等效的隐式转换(例如在您的查询中),从字符串值到时间戳,那么 Oracle 会将转换隐式转换为等效的:

TO_TIMESTAMP(
  value,
  (SELECT value FROM NLS_SESSION_PARAMETERS WHERE parameter = 'NLS_TIMESTAMP_FORMAT')
)

如果您有示例数据:

CREATE TABLE table_name (value) AS
SELECT '2022-01-21 2:49:38.251'  FROM DUAL UNION ALL
SELECT '22-01-21 2:49:38.251000' FROM DUAL;

您可以使用以下方法查看它的运行情况:

ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'DD-MON-RR HH24.MI.SSXFF';

SELECT value,
       TO_CHAR(
         CAST(value AS TIMESTAMP),
         'YYYY-MM-DD HH24:MI:SS.FF'
       ) AS converted_value
FROM   table_name;

输出错误:

ORA-01843: not a valid month

因为,给定 string-to-date conversion rulesMON 格式模型也将匹配 MONTH 但它不会匹配数字 MM 格式,因此 01月产生错误。

但是:

ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'RR-MM-DD HH24:MI:SSXFF'

SELECT value,
       TO_CHAR(
         CAST(value AS TIMESTAMP),
         'YYYY-MM-DD HH24:MI:SS.FF'
       ) AS converted_value
FROM   table_name;

输出:

VALUE CONVERTED_VALUE
2022-01-21 2:49:38.251 2022-01-21 02:49:38.251000
22-01-21 2:49:38.251000 2022-01-21 02:49:38.251000

两行都按预期转换。


如果您使用:

ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SSXFF'

SELECT value,
       TO_CHAR(
         CAST(value AS TIMESTAMP),
         'YYYY-MM-DD HH24:MI:SS.FF'
       ) AS converted_value
FROM   table_name;

则输出为:

VALUE CONVERTED_VALUE
2022-01-21 2:49:38.251 2022-01-21 02:49:38.251000
22-01-21 2:49:38.251000 0022-01-21 02:49:38.251000

并且第一行按预期转换,但第二行 YYYY 格式模型匹配 22 并给出 22 AD 而不是 2022 AD(正如预期的那样)。


如果您想与时间戳进行比较,则使用显式转换:

SELECT *
FROM   EMPLOYEE
WHERE  CREATE_TIME = TO_TIMESTAMP(
                       '2022-01-21 2:49:38.251',
                       'YYYY-MM-DD HH24:MI:SSXFF'
                     )

或时间戳文字:

SELECT *
FROM   EMPLOYEE
WHERE  CREATE_TIME = TIMESTAMP '2022-01-21 02:49:38.251'

如果您依赖 NLS 参数,那么您的查询对于不同的会话(有时甚至对于同一用户的不同会话)可能会有不同的(和意外的)行为。

db<>fiddle here