具有日期的 Dbeaver 和 OSD 客户端之间的不同行为
Different behavior between Dbeaver and OSD clients with dates
我使用 Oracle Database 11g 11.2.0.4.0 版
执行此查询时:
SELECT
CASE WHEN (DATE '2005-11-25') IN ('25/11/05') THEN
'TRUE'
ELSE
'FALSE'
END
FROM DUAL;
我从 Oracle SQL Developer 21.4.1.349(法语版)获得 FALSE
,从 DBeaver 21.1.4.202108020335(法语版)获得 TRUE
。
我知道我将日期与字符串进行比较,所以我认为结果应该是 FALSE
,但这可能取决于实现。
为什么 Oracle 在这些情况下会做出不同的回答? DBeaver 是否在提交请求之前对日期进行预处理?
因为您将日期与字符串进行比较,所以您使用会话的 NLS 设置将字符串隐式转换为日期。这些可能来自您的语言环境,或者可能由您的客户明确设置,例如通过偏好。
如果 NLS_DATE_FORMAT
设置为 'DD/MM/YYYY',则“25/11/05”将转换为 0005-11-25,因此比较结果为假。如果设置为'DD/MM/YY'(或'DD/MM/RR',或'DD/MM/RRRR')则转换为2005-11-25,因此比较为真。
您不应依赖隐式转换或 NLS 设置 - 因为您无法控制运行您代码的人的环境。
这取决于 NLS_DATE_FORMAT
会话正在使用。
Oracle 会将您的查询隐式转换为等效于:
SELECT CASE WHEN DATE '2005-11-25' IN (
TO_DATE(
'25/11/05',
(SELECT VALUE FROM NLS_SESSION_PARAMETERS WHERE PARAMETER = 'NLS_DATE_FORMAT')
)
)
THEN 'TRUE'
ELSE 'FALSE'
END
FROM DUAL;
由于它依赖于从字符串到日期的隐式转换,因此 Oracle 将在转换中使用会话的日期格式。
例如:
ALTER SESSION SET NLS_DATE_FORMAT = 'DD/MM/RR';
SELECT CASE WHEN DATE '2005-11-25' IN ('25/11/05')
THEN 'TRUE'
ELSE 'FALSE'
END
FROM DUAL;
输出:TRUE
但是:
ALTER SESSION SET NLS_DATE_FORMAT = 'DD/MM/YYYY HH24:MI:SS';
SELECT CASE WHEN DATE '2005-11-25' IN ('25/11/05')
THEN 'TRUE'
ELSE 'FALSE'
END
FROM DUAL;
输出:FALSE
你可以看到为什么使用:
SELECT TO_CHAR(CAST('25/11/05' AS DATE), 'YYYY-MM-DD HH24:MI:SS') FROM DUAL;
输出 0005-11-25 00:00:00
并且是 1 世纪,而不是 21 世纪,因为隐式转换使用 DD/MM/YYYY HH24:MI:SS
格式模型并期望 4 位数年份并获得输入 05
并假定它是公元 5 年的正确值,而不是当前世纪的第 5 年(您将使用 YY
格式模型获得)。
最好的解决方案是不依赖隐式字符串到日期的转换。
要么使用日期文字:
SELECT CASE WHEN DATE '2005-11-25' IN (DATE '2005-11-25')
THEN 'TRUE'
ELSE 'FALSE'
END
FROM DUAL;
或者,为转换提供明确的格式模型:
SELECT CASE WHEN DATE '2005-11-25' IN (TO_DATE('25/11/05', 'DD/MM/RR'))
THEN 'TRUE'
ELSE 'FALSE'
END
FROM DUAL;
db<>fiddle here
我使用 Oracle Database 11g 11.2.0.4.0 版
执行此查询时:
SELECT
CASE WHEN (DATE '2005-11-25') IN ('25/11/05') THEN
'TRUE'
ELSE
'FALSE'
END
FROM DUAL;
我从 Oracle SQL Developer 21.4.1.349(法语版)获得 FALSE
,从 DBeaver 21.1.4.202108020335(法语版)获得 TRUE
。
我知道我将日期与字符串进行比较,所以我认为结果应该是 FALSE
,但这可能取决于实现。
为什么 Oracle 在这些情况下会做出不同的回答? DBeaver 是否在提交请求之前对日期进行预处理?
因为您将日期与字符串进行比较,所以您使用会话的 NLS 设置将字符串隐式转换为日期。这些可能来自您的语言环境,或者可能由您的客户明确设置,例如通过偏好。
如果 NLS_DATE_FORMAT
设置为 'DD/MM/YYYY',则“25/11/05”将转换为 0005-11-25,因此比较结果为假。如果设置为'DD/MM/YY'(或'DD/MM/RR',或'DD/MM/RRRR')则转换为2005-11-25,因此比较为真。
您不应依赖隐式转换或 NLS 设置 - 因为您无法控制运行您代码的人的环境。
这取决于 NLS_DATE_FORMAT
会话正在使用。
Oracle 会将您的查询隐式转换为等效于:
SELECT CASE WHEN DATE '2005-11-25' IN (
TO_DATE(
'25/11/05',
(SELECT VALUE FROM NLS_SESSION_PARAMETERS WHERE PARAMETER = 'NLS_DATE_FORMAT')
)
)
THEN 'TRUE'
ELSE 'FALSE'
END
FROM DUAL;
由于它依赖于从字符串到日期的隐式转换,因此 Oracle 将在转换中使用会话的日期格式。
例如:
ALTER SESSION SET NLS_DATE_FORMAT = 'DD/MM/RR';
SELECT CASE WHEN DATE '2005-11-25' IN ('25/11/05')
THEN 'TRUE'
ELSE 'FALSE'
END
FROM DUAL;
输出:TRUE
但是:
ALTER SESSION SET NLS_DATE_FORMAT = 'DD/MM/YYYY HH24:MI:SS';
SELECT CASE WHEN DATE '2005-11-25' IN ('25/11/05')
THEN 'TRUE'
ELSE 'FALSE'
END
FROM DUAL;
输出:FALSE
你可以看到为什么使用:
SELECT TO_CHAR(CAST('25/11/05' AS DATE), 'YYYY-MM-DD HH24:MI:SS') FROM DUAL;
输出 0005-11-25 00:00:00
并且是 1 世纪,而不是 21 世纪,因为隐式转换使用 DD/MM/YYYY HH24:MI:SS
格式模型并期望 4 位数年份并获得输入 05
并假定它是公元 5 年的正确值,而不是当前世纪的第 5 年(您将使用 YY
格式模型获得)。
最好的解决方案是不依赖隐式字符串到日期的转换。
要么使用日期文字:
SELECT CASE WHEN DATE '2005-11-25' IN (DATE '2005-11-25')
THEN 'TRUE'
ELSE 'FALSE'
END
FROM DUAL;
或者,为转换提供明确的格式模型:
SELECT CASE WHEN DATE '2005-11-25' IN (TO_DATE('25/11/05', 'DD/MM/RR'))
THEN 'TRUE'
ELSE 'FALSE'
END
FROM DUAL;
db<>fiddle here