【问题标题】:Getting a date from a 4 digit strange date format in Oracle SQL从 Oracle SQL 中的 4 位奇怪日期格式获取日期
【发布时间】:2020-06-26 09:02:31
【问题描述】:

我从解码 codebar-128 收到一个字符串,一旦我解析读取的代码中的所有数据,我会得到一个奇怪的 4 位数格式的日期:'YDDD'

“Y”数字代表年份的最后一位数字 (0-9)。 “DDD”数字表示一年中的第几天 (1-366)。

问题在于 Year 的值不明确。解决该问题的规则必须如下:

  1. 为“Y”数字计算的年份必须是最接近 Sysdate 年份的年份。
  2. “Y”数字与 Sysdate 年份和计算年份的差永远不会大于 4。

我的代码:

SELECT SYSDATE, TO_DATE('0213', 'YDDD'), TO_DATE('1212', 'YDDD'), 
        TO_DATE('2212', 'YDDD'), TO_DATE('3212', 'YDDD'), TO_DATE('4213', 'YDDD'),
        TO_DATE('6212', 'YDDD'), TO_DATE('7212', 'YDDD'), TO_DATE('8213', 'YDDD'),
        TO_DATE('9212', 'YDDD')
FROM dual;

这是我需要得到的:

+-----------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
|  SYSDATE  | TO_DATE('20213','YYDDD') | TO_DATE('21212','YYDDD') | TO_DATE('22212','YYDDD') | TO_DATE('23212','YYDDD') | TO_DATE('24213','YYDDD') | TO_DATE('16213','YYDDD') | TO_DATE('17212','YYDDD') | TO_DATE('18212','YYDDD') | TO_DATE('19212','YYDDD') |
+-----------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
| 26-JUN-20 | 31-JUL-20                | 31-JUL-21                | 31-JUL-22                | 31-JUL-23                | 31-JUL-24                | 31-JUL-16                | 31-JUL-17                | 31-JUL-18                | 31-JUL-19                |
+-----------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+

如你所见,如果我有倒数第二个年份,那就没有问题了。

这是我真正得到的:

+-----------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+
|  SYSDATE  | TO_DATE('0213','YDDD') | TO_DATE('1212','YDDD') | TO_DATE('2212','YDDD') | TO_DATE('3212','YDDD') | TO_DATE('4213','YDDD') | TO_DATE('6212','YDDD') | TO_DATE('7212','YDDD') | TO_DATE('8213','YDDD') | TO_DATE('9212','YDDD') |
+-----------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+
| 26-JUN-20 | 31-JUL-20              | 31-JUL-21              | 31-JUL-22              | 31-JUL-23              | 31-JUL-24              | 31-JUL-26              | 31-JUL-27              | 31-JUL-28              | 31-JUL-29              |
+-----------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+

【问题讨论】:

  • 如果您的规则 (1) 和 (2) 适用,为什么 '1' 变成 2001 而不是 2021?那不是更接近今天,而且距离今天还有 4 年多的时间?我不确定这是否只是你如何生成它的问题。通过复制您实际获得的内容并进行必要的更改(可能会解释每一项)来显示您的预期输出可能会有所帮助?
  • @AlexPoole 你没事! “1”必须是“2021”。我已经修好了。
  • 不用说您应该尽快将您的设计更改为正确的DATE(或TIMESTAMP)数据类型。

标签: sql oracle sql-function to-date sql-date-functions


【解决方案1】:

可以将个位数的值与当年的最后一位进行比较,如果相差大于4,调整不过10年。但它需要双向;一旦“今天”是 2026 年,您将增加 10 年。

select column_value as val,
  to_date(column_value, 'YDDD') as dt1,
  to_number(substr(column_value, 1, 1)) as y,
  mod(extract(year from sysdate), 10) as yy,
  case
    when to_number(substr(column_value, 1, 1)) - mod(extract(year from sysdate), 10) > 4 then -10
    when mod(extract(year from sysdate), 10) - to_number(substr(column_value, 1, 1)) > 4 then 10
    else 0
  end as adj,
  to_date(column_value, 'YDDD')
    + case
        when to_number(substr(column_value, 1, 1)) - mod(extract(year from sysdate), 10) > 4 then -10
        when mod(extract(year from sysdate), 10) - to_number(substr(column_value, 1, 1)) > 4 then 10
        else 0
      end * interval '1' year as dt2,
  add_months(to_date(column_value, 'YDDD'),
    12 * case
        when to_number(substr(column_value, 1, 1)) - mod(extract(year from sysdate), 10) > 4 then -10
        when mod(extract(year from sysdate), 10) - to_number(substr(column_value, 1, 1)) > 4 then 10
        else 0
      end) as dt2
from table(sys.odcivarchar2list('0213', '1212', '2212', '3212', '4213',
                                '5212', '6212', '7212', '8213', '9212'));

得到

VAL  DT1                 Y         YY        ADJ DT2        DT2       
---- ---------- ---------- ---------- ---------- ---------- ----------
0213 2020-07-31          0          0          0 2020-07-31 2020-07-31
1212 2021-07-31          1          0          0 2021-07-31 2021-07-31
2212 2022-07-31          2          0          0 2022-07-31 2022-07-31
3212 2023-07-31          3          0          0 2023-07-31 2023-07-31
4213 2024-07-31          4          0          0 2024-07-31 2024-07-31
5212 2025-07-31          5          0        -10 2015-07-31 2015-07-31
6212 2026-07-31          6          0        -10 2016-07-31 2016-07-31
7212 2027-07-31          7          0        -10 2017-07-31 2017-07-31
8213 2028-07-31          8          0        -10 2018-07-31 2018-07-31
9212 2029-07-31          9          0        -10 2019-07-31 2019-07-31

我尚未验证未来一年的行为,因此您可能需要根据需要对其进行测试和调整。

【讨论】:

    【解决方案2】:

    将其拆分为多个 with 子句以便于理解,如果需要,您可以将其加入单个查询中。

    WITH sampledata (dt) AS
    (
     SELECT '0213' FROM DUAL UNION
     SELECT '1212' FROM DUAL UNION
     SELECT '2212' FROM DUAL UNION
     SELECT '3212' FROM DUAL UNION
     SELECT '4213' FROM DUAL UNION
     SELECT '5213' FROM DUAL UNION
     SELECT '6212' FROM DUAL UNION
     SELECT '7212' FROM DUAL UNION
     SELECT '8213' FROM DUAL UNION
     SELECT '9212' FROM DUAL 
    ), parsed_sampledata (yr, ddd) AS
    (
    SELECT substr(d.dt,1, 1) + TO_CHAR(SYSDATE,'YY') as yr, substr(d.dt,2,3) as ddd
      FROM sampledata d
    )
    SELECT TO_DATE(ddd||yr - (CASE WHEN yr - TO_CHAR(SYSDATE,'YY') < 5 THEN 0 ELSE 10 END),'DDDYY')  
      FROM parsed_sampledata d;
    
    31-JUL-2020
    31-JUL-2021
    31-JUL-2022
    31-JUL-2023
    31-JUL-2024
    01-AUG-2015
    30-JUL-2016
    31-JUL-2017
    01-AUG-2018
    31-JUL-2019
    

    【讨论】:

      【解决方案3】:

      这应该会给你一些想法:

      WITH DATES_LIST AS
      (
       SELECT '0213' AS D FROM DUAL UNION
       SELECT '1212' AS D FROM DUAL UNION
       SELECT '2212' AS D FROM DUAL UNION
       SELECT '3212' AS D FROM DUAL UNION
       SELECT '4213' AS D FROM DUAL UNION
       SELECT '5213' AS D FROM DUAL UNION
       SELECT '6213' AS D FROM DUAL UNION
       SELECT '7212' AS D FROM DUAL UNION
       SELECT '8212' AS D FROM DUAL UNION
       SELECT '9212' AS D FROM DUAL 
      )
      SELECT  TO_DATE(REGEXP_REPLACE(D,'^\d{1}',
              CASE WHEN BOTT_R <= UPP_R THEN BOT ELSE UPP END),'YYDDD') AS YEAR 
              FROM (
      select D,(TO_CHAR(SYSDATE,'RR') - 10) + regexp_substr(D, '^\d{1}') BOT,
             ABS((TO_CHAR(SYSDATE,'RR') - 10) + regexp_substr(D, '^\d{1}')-TO_CHAR(SYSDATE,'RR')) BOTT_R,
             TO_CHAR(SYSDATE,'RR') + regexp_substr(D, '^\d{1}') UPP,
             (TO_CHAR(SYSDATE,'RR') + regexp_substr(D, '^\d{1}')) - TO_CHAR(SYSDATE,'RR') UPP_R
              from DATES_LIST);
      

      如果您需要转换为许多变量(很多),我的建议是创建一个 DETERMINISTIC 函数。

      问候。

      【讨论】:

      • 我不确定这是否符合规则;它仍然为您的示例提供 2001,而不是 2021?将 1 转换为 01 似乎并没有增加太多(并且可以通过简单的字符串连接来完成),但不确定这在大局中是否重要。
      • 抱歉,请再检查一次,我在之前的示例中确实省略了部分逻辑。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-22
      • 2013-02-21
      相关资源
      最近更新 更多