从 Oracle 中的 4 位奇怪日期格式获取日期 SQL

Getting a date from a 4 digit strange date format in Oracle SQL

我从解码 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              |
+-----------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+

将其拆分为多个 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

您可以将个位数的数值与当年的最后一位进行比较,如果相差超过4,则调整为10年。但这需要双向进行;一旦 'today' 进入 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

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

这应该会给你一些想法:

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 函数。

此致。