英文:
PL/SQL datetime update set results in time 00:00:00
问题
我正在尝试使用以下SQL来设置一个比end_ts日期时间早一秒的日期时间:
UPDATE Table1 SET
UPDTD_DT=(SELECT TO_DATE(END_TS-1/(24*60*60),'DD/MM/YYYY HH24:MI:SS')
FROM View1) WHERE id = '123';
但是在运行此更新后,日期时间变为00:00:00。我已显示并检查了TO_DATE(END_TS-1/(246060),'DD/MM/YYYY HH24:MI:SS'),看起来没问题。
SELECT TO_DATE(END_TS-1/(24*60*60),'DD/MM/YYYY HH24:MI:SS') FROM View1 is 25/03/2022 10:14:44
任何建议将不胜感激。
英文:
I am trying to set a datetime one second less than the end_ts datetime with sql as below
UPDATE Table1 SET
UPDTD_DT=(SELECT TO_DATE(END_TS-1/(24*60*60),'DD/MM/YYYY HH24:MI:SS')
FROM View1) WHERE id = '123';
But after ran this update, the datetime is 00:00:00. I displayed and checked the TO_DATE(END_TS-1/(246060),'DD/MM/YYYY HH24:MI:SS') which looks fine.
SELECT TO_DATE(END_TS-1/(24*60*60),'DD/MM/YYYY HH24:MI:SS') FROM View1 is 25/03/2022 10:14:44
Any advice would be highly appreciated.
答案1
得分: 6
永远不要在已经是DATE
(或TIMESTAMP
)的值上使用 TO_DATE
。最好的情况下,它将不起作用,最坏的情况下,它将引发异常,或者如您所发现的那样,产生意外的输出。
您需要:
UPDATE Table1
SET UPDTD_DT = (SELECT END_TS - INTERVAL '1' SECOND FROM View1)
WHERE id = '123';
或者使用 INTERVAL '1' SECOND
而不是 1/(24*60*60)
,但在代码审查期间,后者可能更容易理解您的意思。
如果您使用:
SELECT TO_DATE(END_TS-1/(24*60*60),'DD/MM/YYYY HH24:MI:SS')
FROM View1
并且 END_TS
是一个 DATE
列,那么 TO_DATE
期望其第一个参数是一个字符串,因此您的 DATE
值将被隐式转换为字符串,使用日期到字符串转换的默认格式模型(NLS_DATE_FORMAT
会话参数)然后再转换回 DATE
,查询实际上是:
SELECT TO_DATE(
TO_CHAR(
END_TS-1/(24*60*60),
(SELECT value FROM NLS_SESSION_PARAMETERS WHERE parameter = 'NLS_DATE_FORMAT')
),
'DD/MM/YYYY HH24:MI:SS'
)
FROM View1
如果 NLS_DATE_FORMAT
不匹配 'DD/MM/YYYY HH24:MI:SS'
格式模型,那么将会引发异常。如果匹配但不显示时间组件(即 NLS_DATE_FORMAT
是 DD/MM/YYYY
),那么将使用午夜的默认时间组件。
从理论上讲,您可以通过更改用户的 NLS_DATE_FORMAT
会话参数来解决此问题:
ALTER SESSION SET NLS_DATE_FORMAT = 'DD/MM/YYYY HH24:MI:SS';
但随后您需要为每个运行查询的用户设置它,而用户可以随时更改自己的会话参数,因此您必须确保每次运行查询时都设置它。因此,这不被认为是良好的做法。
更好的做法是不要对 DATE
值使用 TO_DATE
,避免隐式转换为字符串。
英文:
NEVER use TO_DATE
on a value that is already a DATE
(or TIMESTAMP
). At best, it will do nothing and, at worst, it will raise an exception or, as you are finding, give an unexpected output.
You want:
UPDATE Table1
SET UPDTD_DT = (SELECT END_TS - INTERVAL '1' SECOND FROM View1)
WHERE id = '123';
Or 1/(24*60*60)
instead of INTERVAL '1' SECOND
, but the latter is probably easier to comprehend what you mean during a code review.
If you use:
SELECT TO_DATE(END_TS-1/(24*60*60),'DD/MM/YYYY HH24:MI:SS')
FROM View1
and END_TS
is a DATE
column then TO_DATE
expects a string as its first argument so your DATE
value will be implicitly cast to a string using the default format model for date-to-string conversions (the NLS_DATE_FORMAT
session parameter) before being converted back to a DATE
and the query is effectively:
SELECT TO_DATE(
TO_CHAR(
END_TS-1/(24*60*60),
(SELECT value FROM NLS_SESSION_PARAMETERS WHERE parameter = 'NLS_DATE_FORMAT')
),
'DD/MM/YYYY HH24:MI:SS'
)
FROM View1
If the NLS_DATE_FORMAT
does not match the 'DD/MM/YYYY HH24:MI:SS'
format model then you will get an exception. If it does match but does not display the time component (i.e. the NLS_DATE_FORMAT
is DD/MM/YYYY
) then the default time component of midnight will be used.
Theoretically, you could solve the problem by changing the user's NLS_DATE_FORMAT
session parameter:
ALTER SESSION SET NLS_DATE_FORMAT = 'DD/MM/YYYY HH24:MI:SS';
But then you would need to set it for every user who runs the query and users can change their own session parameters at any time so you would have to ensure it is set each time you run the query. So this is not considered good practice.
It is considered better practice to not use TO_DATE
to a DATE
value and avoid the implicit cast to a string.
答案2
得分: 4
发生这种情况的原因是您在类型为DATE
的列上使用了TO_DATE
。没有必要这样做。
请查看以下示例(TO_CHAR
用于显示目的):
SELECT TO_CHAR(
TO_DATE(sysdate-1/(24*60*60),'DD/MM/YYYY HH24:MI:SS'),
'DD-MON-YYYY HH24:MI') as 不必要的TODATE,
TO_CHAR(
sysdate-1/(24*60*60),
'DD-MON-YYYY HH24:MI') as 正确的代码
FROM dual;
不必要的TODATE 正确的代码
-------------------------- --------------------------
12-JUN-2023 00:00 12-JUN-2023 08:06
因此,要修复您的问题:
UPDATE Table1 SET
UPDTD_DT=(SELECT END_TS-1/(24*60*60)
FROM View1) WHERE id = '123';
或者... 使用INTERVAL
来添加/减去。
UPDATE Table1
SET
UPDTD_DT= (SELECT END_TS - INTERVAL '1' SECOND
FROM View1)
WHERE id = '123';
英文:
Reason that is happening is that you're doing a TO_DATE
on a column of type DATE
. There is no need to do that.
Check the following example (the wrapping TO_CHAR is only for display purposes):
SELECT TO_CHAR(
TO_DATE(sysdate-1/(24*60*60),'DD/MM/YYYY HH24:MI:SS'),
'DD-MON-YYYY HH24:MI') as unneeded_todate,
TO_CHAR(
sysdate-1/(24*60*60),
'DD-MON-YYYY HH24:MI') as correct_code
FROM dual;
UNNEEDED_TODATE CORRECT_CODE
-------------------------- --------------------------
12-JUN-2023 00:00 12-JUN-2023 08:06
So, to fix your issue:
UPDATE Table1 SET
UPDTD_DT=(SELECT END_TS-1/(24*60*60)
FROM View1) WHERE id = '123';
Or... use INTERVAL
to add/substract.
UPDATE Table1
SET
UPDTD_DT= (SELECT END_TS - INTERVAL '1' SECOND
FROM View1)
WHERE id = '123';
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论