PL/SQL日期时间更新设置结果为时间00:00:00。

huangapple go评论82阅读模式
英文:

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_FORMATDD/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.

fiddle

答案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';

huangapple
  • 本文由 发表于 2023年6月12日 15:49:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/76454554.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定