PL/SQL get the real number of weeks in a year in next 6 months

Viewed 43

How can I get the real number of week of the year in next 6 months?

Example:
13.09.2022 is the 38th week of the year
17.09.2022 is the 38th week of the year
19.09.2022 is the 39th week of the year
.
.
01.01.2023 is the 1th week of the year (Sunday)
02.01.2023 is the 2th week of the year (Monday)
09.01.2023 is the 3th week of the year
.
Next 6 Months

I already know the real number of the week in a year but not in next 6 months, just to the end of the year in the below code.

SELECT LISTAGG (
                         DISTINCT TO_CHAR (TRUNC (mydate, 'MONTH'), 'MONTH'),
                         ', ')                              AS MONTHS,
                     TO_CHAR (nr_of_sundays + 1, 'fm09')    week_num
                FROM (SELECT mydate,
                               (  (  TRUNC (mydate, 'day')
                                   - TRUNC (TRUNC (mydate, 'yyyy'), 'day'))
                                / 7)
                             + CASE
                                   WHEN TO_CHAR (TRUNC (mydate, 'YYYY'), 'day') =
                                        'sun'
                                   THEN
                                       1
                                   ELSE
                                       0
                               END    AS nr_of_sundays
                        FROM (    SELECT TRUNC (SYSDATE, 'yy') - 1 + LEVEL    AS mydate
                                    FROM DUAL
                              CONNECT BY LEVEL <=
                                           TRUNC (ADD_MONTHS (SYSDATE, 12),
                                                  'yy')
                                         - TRUNC (SYSDATE, 'yy')))
               WHERE TO_DATE (mydate, 'DD/MM/YY') >=
                     TO_DATE (SYSDATE, 'DD/MM/YY')
            GROUP BY TO_CHAR (nr_of_sundays + 1, 'fm09')
            ORDER BY TO_CHAR (nr_of_sundays + 1, 'fm09');

Result of the above code

Could you guys help me?

2 Answers

Here is the original query modified to only show 6 months of data for the next 6 months, starting at the first day of the current month:

SELECT LISTAGG (
                         DISTINCT TO_CHAR (TRUNC (mydate, 'MONTH'), 'MONTH'),
                         ', ')                              AS MONTHS,
                     TO_CHAR (nr_of_sundays + 1, 'fm09')    week_num
                FROM (SELECT mydate,
                               (  (  TRUNC (mydate, 'day')
                                   - TRUNC (TRUNC (mydate, 'yyyy'), 'day'))
                                / 7)
                             + CASE
                                   WHEN TO_CHAR (TRUNC (mydate, 'YYYY'), 'day') =
                                        'sun'
                                   THEN
                                       1
                                   ELSE
                                       0
                               END    AS nr_of_sundays
                        FROM (    SELECT TRUNC (SYSDATE, 'mm') - 1 + LEVEL    AS mydate --
                                    FROM DUAL
                              CONNECT BY LEVEL <=
                                           TRUNC (ADD_MONTHS (SYSDATE, 6), --
                                                  'mm') --
                                         - TRUNC (SYSDATE, 'mm'))) --
               WHERE TO_DATE (mydate, 'DD/MM/YY') >=
                     TO_DATE (SYSDATE, 'DD/MM/YY')
            GROUP BY TO_CHAR (nr_of_sundays + 1, 'fm09')
            ORDER BY TO_CHAR (nr_of_sundays + 1, 'fm09');
            
            SELECT TRUNC (SYSDATE, 'mm') - 1 + LEVEL    AS mydate 
                                    FROM DUAL
                              CONNECT BY LEVEL <=
                                           TRUNC (ADD_MONTHS (SYSDATE, 6), 
                                                  'mm') 
                                         - TRUNC (SYSDATE, 'mm');

MONTHS      WEEK_NUM

JANUARY     01
JANUARY     02
JANUARY     03
JANUARY     04
FEBRUARY , JANUARY      05
FEBRUARY    06
FEBRUARY    07
FEBRUARY    08
FEBRUARY    09
SEPTEMBER   38
SEPTEMBER   39
OCTOBER  , SEPTEMBER    40
...

However, because the results only have columns "months" and "week_num" and the order by is by week_num - it will look odd. The first row will be the first week_num of next year. You could add the year to each row. Here is a rewritten query using exactly the same logic but including the year for each row:

WITH date_range (mydate) AS
(
   SELECT TRUNC (SYSDATE, 'mm') - 1 + LEVEL
     FROM DUAL
         CONNECT BY LEVEL <=  TRUNC (ADD_MONTHS (SYSDATE, 6),'mm') - TRUNC (SYSDATE, 'mm')
), weeknum_per_date (mydate, nr_of_sundays) AS
(
   SELECT mydate,
          (TRUNC (mydate, 'day') - TRUNC (TRUNC (mydate, 'yyyy'), 'day')) / 7
          + CASE WHEN TO_CHAR (TRUNC (mydate, 'YYYY'), 'day') = 'sun' THEN 1 ELSE 0 END    AS nr_of_sundays 
     FROM date_range
)
SELECT TO_CHAR(mydate, 'YYYY') as year, LISTAGG (DISTINCT TO_CHAR (TRUNC (mydate, 'MONTH'), 'MONTH'),
                         ', ')                              AS MONTHS,
                     TO_CHAR (nr_of_sundays + 1, 'fm00')    week_num
  FROM weeknum_per_date                                            
               WHERE TO_DATE (mydate, 'DD/MM/YY') >=
                     TO_DATE (SYSDATE, 'DD/MM/YY')
            GROUP BY TO_CHAR (nr_of_sundays + 1, 'fm00'), TO_CHAR(mydate, 'YYYY')
            ORDER BY TO_CHAR(mydate, 'YYYY'), TO_CHAR (nr_of_sundays + 1, 'fm00'); 

year    month      week_num
2022    SEPTEMBER   38
2022    SEPTEMBER   39
2022    OCTOBER  , SEPTEMBER    40
2022    OCTOBER     41
2022    OCTOBER     42
2022    OCTOBER     43
2022    OCTOBER     44
2022    NOVEMBER , OCTOBER      45
2022    NOVEMBER    46
2022    NOVEMBER    47
2022    NOVEMBER    48
2022    DECEMBER , NOVEMBER     49
2022    DECEMBER    50
2022    DECEMBER    51
2022    DECEMBER    52
2022    DECEMBER    53
2023    JANUARY     01
2023    JANUARY     02
...

If you want the ISO week (which starts on a Monday and ends on a Sunday and belongs to whichever year contains the majority of its days) then:

SELECT CASE
       WHEN EXTRACT(MONTH FROM week_start)
            = EXTRACT(MONTH FROM week_end)
       THEN TO_CHAR(week_start, 'fmMONTH')
       ELSE TO_CHAR(week_start, 'fmMONTH')
            || ', ' || TO_CHAR(week_end, 'fmMONTH')
       END as month,
       TO_CHAR(week_start, 'IW') AS iso_week,
       TO_CHAR(week_start, 'IYYY') AS iso_year
FROM   (
  SELECT TRUNC(ADD_MONTHS(SYSDATE, -12), 'IW') + (LEVEL - 1) * 7 AS week_start,
         TRUNC(ADD_MONTHS(SYSDATE, -12), 'IW') + LEVEL * 7 - 1 AS week_end
  FROM   DUAL
  CONNECT BY TRUNC(ADD_MONTHS(SYSDATE, -12), 'IW') + (LEVEL - 1) * 7 <= TRUNC(SYSDATE)
);

Which outputs:

MONTH ISO_WEEK ISO_YEAR
SEPTEMBER 37 2021
SEPTEMBER 38 2021
SEPTEMBER, OCTOBER 39 2021
OCTOBER 40 2021
OCTOBER 41 2021
OCTOBER 42 2021
OCTOBER 43 2021
NOVEMBER 44 2021
NOVEMBER 45 2021
NOVEMBER 46 2021
NOVEMBER 47 2021
NOVEMBER, DECEMBER 48 2021
DECEMBER 49 2021
DECEMBER 50 2021
DECEMBER 51 2021
DECEMBER, JANUARY 52 2021
JANUARY 01 2022
JANUARY 02 2022
JANUARY 03 2022
JANUARY 04 2022
JANUARY, FEBRUARY 05 2022
FEBRUARY 06 2022
FEBRUARY 07 2022
FEBRUARY 08 2022
FEBRUARY, MARCH 09 2022
MARCH 10 2022
MARCH 11 2022
MARCH 12 2022
MARCH, APRIL 13 2022
APRIL 14 2022
APRIL 15 2022
APRIL 16 2022
APRIL, MAY 17 2022
MAY 18 2022
MAY 19 2022
MAY 20 2022
MAY 21 2022
MAY, JUNE 22 2022
JUNE 23 2022
JUNE 24 2022
JUNE 25 2022
JUNE, JULY 26 2022
JULY 27 2022
JULY 28 2022
JULY 29 2022
JULY 30 2022
AUGUST 31 2022
AUGUST 32 2022
AUGUST 33 2022
AUGUST 34 2022
AUGUST, SEPTEMBER 35 2022
SEPTEMBER 36 2022
SEPTEMBER 37 2022

fiddle

Related