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 rules,MON
格式模型也将匹配 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
以下两个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 rules,MON
格式模型也将匹配 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