简体   繁体   English

Oracle SQL 从年和周数中获取日期

[英]Oracle SQL Get date from year and week number

I have year and week numbers in a SQL table in one column with a format as VERSION_YEAR_WEEKNUMBER I can use the SUBSTR function to just get the YEAR_WEEKNUMBER , but I then need to convert that to a date and I'm not sure how to convert a weeknumber to a date.我在SUBSTR表中有年份和周YEAR_WEEKNUMBER ,格式为VERSION_YEAR_WEEKNUMBER weeknumber 到日期。

Ideally the date would be a Thursday, but right now I'd just like to convert a weeknumber to any date that's in that week and year combination.理想情况下,日期是星期四,但现在我只想将周数转换为该周和年组合中的任何日期。

Sample data would just be "VERSION_YEAR_WEEKNUMBER" eg VERSION_2020_10.示例数据只是“VERSION_YEAR_WEEKNUMBER”,例如 VERSION_2020_10。 Meaning, 2020, 10th week (ISO) of the year.意思是,2020 年,一年中的第 10 周 (ISO)。 Desired result should be one date between 02-MAR-20 and 08-MAR-20期望的结果应该是 02-MAR-20 和 08-MAR-20 之间的一个日期

You can extract the year and week numbers separately:您可以分别提取年份和周数:

substr(your_column, instr(your_column, '_', -1, 2) + 1, 4)
substr(your_column, instr(your_column, '_', -1, 1) + 1)

Then convert the year number to the first day of the ISO year:然后将年份编号转换为 ISO 年份的第一天:

trunc(to_date(substr(your_column, instr(your_column, '_', -1, 2) + 1, 4), 'YYYY'), 'IYYY')

and either add the number of weeks - 1 * 7 days to get the Monday of that week, then add 3 for the Thursday;并添加周数 - 1 * 7 天以获得该周的星期一,然后添加 3 作为星期四; or add the number of weeks * 7 days to get the Monday of the following week and subtract four for the Thursday of the week you want:或添加周数 * 7 天以获得下一周的星期一,并为您想要的一周中的星期四减去四:

trunc(to_date(substr(your_column, instr(your_column, '_', -1, 2) + 1, 4), 'YYYY'), 'IYYY')
    + (to_number(substr(your_column, instr(your_column, '_', -1, 1) + 1)) * 7)
    - 4
YOUR_COLUMN     RESULT
--------------- ----------
VERSION_2020_10 2020-03-05
1.2.3_2021_01   2021-01-07
1_2_3_2020_11   2020-03-12
2020_12         2020-03-19
ABC_2020_52     2020-12-24
DEF_2020_53     2020-12-31
GHI_2021_01     2021-01-07
JKL_2021_52     2021-12-30
MNO_2021_53     2022-01-06

db<>fiddle showing the intermediate steps so you can see what's happening. db<>fiddle显示中间步骤,以便您查看发生了什么。

Converting from ISO-Week to year is not trival, because the ISO year may differ from the actual year.将 ISO 周转换为年份并非易事,因为 ISO 年份可能与实际年份不同。

For example 20 18 -12-31 was Week 1 of 20 19 according to ISO-8601.例如,根据 ISO-8601,20 18 -12-31 是 20 19的第 1 周。

I ended up with this function:我最终得到了这个 function:

FUNCTION ISOWeek2Date(YEAR INTEGER, WEEK INTEGER) RETURN DATE DETERMINISTIC IS
    res DATE;
BEGIN
    IF WEEK > 53 OR WEEK < 1 THEN
        RAISE VALUE_ERROR;      
    END IF;
    res := NEXT_DAY(TO_DATE( YEAR || '0104', 'YYYYMMDD' ) - 7, 'MONDAY') + ( WEEK - 1 ) * 7;
    IF TO_CHAR(res, 'fmIYYY') = YEAR THEN
        RETURN res;
    ELSE
        RAISE VALUE_ERROR;
    END IF;
END ISOWeek2Date;

However NEXT_DAY depends on date language of your session, so the solution by @MT0 would be better.但是NEXT_DAY取决于您的 session 的日期语言,因此@MT0 的解决方案会更好。 The logic is the same.逻辑是一样的。

Extract year and week values with regular expression as proposed by @Alex Poole使用@Alex Poole 提出的正则表达式提取年份和星期值

Converting to an ISO week is relatively simple as the 4th of January will always be in the first ISO week of the year.转换为 ISO 周相对简单,因为 1 月 4 日始终是一年中的第一个 ISO 周。 So:所以:

  • Start with the 4th January of your year;从您当年的 1 月 4 日开始;
  • Truncate it back to Monday to be the start of the ISO week;将其截回到星期一作为 ISO 周的开始;
  • Add 3 days and you have Thursday of the first ISO week;再加上 3 天,您就有了 ISO 第一周的星期四; and then接着
  • Add the number of weeks to take it from the 1st week to the required week.添加从第一周到所需周的周数。

Like this:像这样:

SELECT TRUNC( TO_DATE( SUBSTR( value, 9, 4 )||'01-04', 'YYYY-MM-DD' ), 'IW' )
       + INTERVAL '3' DAY
       + INTERVAL '7' DAY * ( SUBSTR( value, 14 ) - 1 )
         AS thursday_of_iso_week
FROM  table_name

Which, for the sample data:其中,对于样本数据:

CREATE TABLE table_name ( value ) AS
SELECT 'VERSION_2020_10' FROM DUAL UNION ALL
SELECT 'VERSION_2019_1' FROM DUAL

Outputs:输出:

 | | THURSDAY_OF_ISO_WEEK | THURSDAY_OF_ISO_WEEK | |:------------------- | |:------------------- | | | 2020-03-05 00:00:00 | 2020-03-05 00:00:00 | | | 2019-01-03 00:00:00 | 2019-01-03 00:00:00 |

If the values do not have fixed character positions then use INSTR to find the underscores:如果值没有固定的字符位置,则使用INSTR查找下划线:

SELECT TRUNC( TO_DATE( SUBSTR( value, INSTR( value, '_' ) + 1, 4 )||'01-04', 'YYYY-MM-DD' ), 'IW' )
       + INTERVAL '3' DAY
       + INTERVAL '7' DAY * ( SUBSTR( value, INSTR( value, '_', -1 ) + 1 ) - 1 )
         AS thursday_of_iso_week
FROM  table_name

Which outputs the same as above.其输出与上述相同。

db<>fiddle here db<> 在这里摆弄

You could join the following CTE to your query您可以将以下 CTE 加入到您的查询中

select weekdate, to_char(weekdate, 'IW-yyyy') week_year from (
SELECT LEVEL weeknr, to_date('2020-01-02', 'yyyy-MM-dd') + numtodsinterval((LEVEL-1)*7, 'day') weekdate
FROM dual
CONNECT BY LEVEL <= 53
);

For the join, use the result of your SUBSTR and WEEK_YEAR of the CTE.对于联接,请使用 CTE 的 SUBSTR 和 WEEK_YEAR 的结果。 WEEKDATE would be the date. WEEKDATE 将是日期。 This CTE generates all the week numbers for all Thursdays of 2020 (January 2nd was the first Thursday in 2020).此 CTE 生成 2020 年所有星期四的所有周数(1 月 2 日是 2020 年的第一个星期四)。 If you need more dates, just use another limit for "LEVEL".如果您需要更多日期,只需对“LEVEL”使用另一个限制。 If you have huge amounts of source data or query the data very frequently, I would recommend to put this in a table (to be done one time) by appending "create table... as" at the beginning and create an index on WEEK_YEAR (and generating enough data so your solution works for a long time).如果您有大量源数据或非常频繁地查询数据,我建议通过在开头附加“create table ... as”并将其放入表中(一次完成)并在 WEEK_YEAR 上创建索引(并生成足够的数据,以便您的解决方案可以长时间工作)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM