# Re: Grouping by Week in Month

From: Charles Hooper <hooperc2000_at_yahoo.com>
Date: Wed, 1 May 2013 04:30:23 -0700 (PDT)

On Tuesday, April 30, 2013 10:32:32 AM UTC-4, artm..._at_gmail.com wrote:
> Hi,
>
> I am using this query to sum data for each week in the month:
>
> SELECT product_description, wk_start, wk_end, accepted, total
>
> FROM (SELECT service_id, TRUNC(date_sent+1,'IW')-1 wk_start, TRUNC(date_sent+1,'IW')+5 wk_end, SUM(accepted) accepted, SUM(list_count) total
>
>
> WHERE date_sent BETWEEN TRUNC(SYSDATE,'MM') AND
>
> TRUNC(LAST_DAY(SYSDATE)+1)
>
> GROUP BY service_id, TRUNC(date_sent+1,'IW')) a, email_products_vw e
>
> WHERE a.service_id = e.email_product_id
>
> ORDER BY wk_start, product_description;
>
> Today is 4/30/2013. The we_end value is 5/4/2013. What I am really looking for is the weeks within the calendar month.
>
> So, the first week for April 2013 should show 4/1 - 4/6. And the last week should show 4/28 - 4/30.
>
> Can anyone help me expand on this to get the date columns to actually be the week date ranges for the actual month?
>
> Thanks!

That looks like an interesting problem - there are 62 weeks this year. :-)

There are probably a couple of different approaches to this problem - one method would be to use a virtual lookup table to determine the week start and end dates per month.

Let's first find the start and end dates of each month of 2013. The month start date is easy to determine, whil ethe month end date is one day less than the start of the next month: SELECT
DUAL
CONNECT BY
LEVEL<=12;

MONTH_STA MONTH_END

```--------- ---------
01-JAN-13 31-JAN-13
01-FEB-13 28-FEB-13
01-MAR-13 31-MAR-13
01-APR-13 30-APR-13
01-MAY-13 31-MAY-13
01-JUN-13 30-JUN-13
01-JUL-13 31-JUL-13
01-AUG-13 31-AUG-13
01-SEP-13 30-SEP-13
01-OCT-13 31-OCT-13
01-NOV-13 30-NOV-13
01-DEC-13 31-DEC-13

```

Next, we should determine the start of the second week - we are able to accomplish that with the NEXT_DAY function to find the next Sunday after the start of the month. The end of the first week will also be calculated, even though it is not used in later calculations. Note that this solution is subject to the NLS settings on the client: SELECT
DUAL
CONNECT BY
LEVEL<=12;

MONTH_STA MONTH_END END_FIRST START_SEC

```--------- --------- --------- ---------
01-JAN-13 31-JAN-13 05-JAN-13 06-JAN-13
01-FEB-13 28-FEB-13 02-FEB-13 03-FEB-13
01-MAR-13 31-MAR-13 02-MAR-13 03-MAR-13
01-APR-13 30-APR-13 06-APR-13 07-APR-13
01-MAY-13 31-MAY-13 04-MAY-13 05-MAY-13
01-JUN-13 30-JUN-13 01-JUN-13 02-JUN-13
01-JUL-13 31-JUL-13 06-JUL-13 07-JUL-13
01-AUG-13 31-AUG-13 03-AUG-13 04-AUG-13
01-SEP-13 30-SEP-13 07-SEP-13 08-SEP-13
01-OCT-13 31-OCT-13 05-OCT-13 06-OCT-13
01-NOV-13 30-NOV-13 02-NOV-13 03-NOV-13
01-DEC-13 31-DEC-13 07-DEC-13 08-DEC-13

```

Next we will need to be able to step through the weeks in each month, potentially 6 weeks per month: SELECT
LEVEL WEEK_NUMBER
FROM
DUAL
CONNECT BY
LEVEL<=6;

WEEK_NUMBER

```          1
2
3
4
5
6

```

Next, we will create a Cartesian join between the two row sources (we will refine the output later): SELECT
MONTH_START,
WN.WEEK_NUMBER,
START_SECOND_WEEK + (WN.WEEK_NUMBER-2)*7 START_WEEK,   START_SECOND_WEEK + (WN.WEEK_NUMBER-1)*7 - 1 END_WEEK FROM
(SELECT
DUAL
CONNECT BY
LEVEL<=12) MONTHS,
(SELECT
LEVEL WEEK_NUMBER
FROM
DUAL
CONNECT BY
LEVEL<=6) WN
WHERE
(START_SECOND_WEEK + (WN.WEEK_NUMBER-2)*7)<=MONTHS.MONTH_END;

MONTH_STA WEEK_NUMBER START_WEE END_WEEK

```--------- ----------- --------- ---------
01-JAN-13           1 30-DEC-12 05-JAN-13
01-JAN-13           2 06-JAN-13 12-JAN-13
01-JAN-13           3 13-JAN-13 19-JAN-13
01-JAN-13           4 20-JAN-13 26-JAN-13
01-JAN-13           5 27-JAN-13 02-FEB-13
01-FEB-13           1 27-JAN-13 02-FEB-13
01-FEB-13           2 03-FEB-13 09-FEB-13
01-FEB-13           3 10-FEB-13 16-FEB-13
01-FEB-13           4 17-FEB-13 23-FEB-13
01-FEB-13           5 24-FEB-13 02-MAR-13
01-MAR-13           1 24-FEB-13 02-MAR-13
01-MAR-13           2 03-MAR-13 09-MAR-13
01-MAR-13           3 10-MAR-13 16-MAR-13
01-MAR-13           4 17-MAR-13 23-MAR-13
01-MAR-13           5 24-MAR-13 30-MAR-13
01-MAR-13           6 31-MAR-13 06-APR-13
01-APR-13           1 31-MAR-13 06-APR-13
01-APR-13           2 07-APR-13 13-APR-13
01-APR-13           3 14-APR-13 20-APR-13
01-APR-13           4 21-APR-13 27-APR-13
01-APR-13           5 28-APR-13 04-MAY-13
01-MAY-13           1 28-APR-13 04-MAY-13
01-MAY-13           2 05-MAY-13 11-MAY-13
01-MAY-13           3 12-MAY-13 18-MAY-13
01-MAY-13           4 19-MAY-13 25-MAY-13
01-MAY-13           5 26-MAY-13 01-JUN-13
01-JUN-13           1 26-MAY-13 01-JUN-13
```
...

Next, we need to make certain that the end of the week is still in the current month, and that the start of the week is also in the current month. I will accomplish that task with the DECODE function: SELECT
MONTH_START,
WN.WEEK_NUMBER,
DECODE(WEEK_NUMBER,1,MONTH_START,START_SECOND_WEEK + (WN.WEEK_NUMBER-2)*7) START_WEEK,   DECODE(SIGN(MONTH_END-(START_SECOND_WEEK + (WN.WEEK_NUMBER-1)*7)),-1,MONTH_END,START_SECOND_WEEK + (WN.WEEK_NUMBER-1)*7 - 1) END_WEEK FROM
(SELECT
DUAL
CONNECT BY
LEVEL<=12) MONTHS,
(SELECT
LEVEL WEEK_NUMBER
FROM
DUAL
CONNECT BY
LEVEL<=6) WN
WHERE
(START_SECOND_WEEK + (WN.WEEK_NUMBER-2)*7)<=MONTHS.MONTH_END;

MONTH_STA WEEK_NUMBER START_WEE END_WEEK

```--------- ----------- --------- ---------
01-JAN-13           1 01-JAN-13 05-JAN-13
01-JAN-13           2 06-JAN-13 12-JAN-13
01-JAN-13           3 13-JAN-13 19-JAN-13
01-JAN-13           4 20-JAN-13 26-JAN-13
01-JAN-13           5 27-JAN-13 31-JAN-13
01-FEB-13           1 01-FEB-13 02-FEB-13
01-FEB-13           2 03-FEB-13 09-FEB-13
01-FEB-13           3 10-FEB-13 16-FEB-13
01-FEB-13           4 17-FEB-13 23-FEB-13
01-FEB-13           5 24-FEB-13 28-FEB-13
01-MAR-13           1 01-MAR-13 02-MAR-13
01-MAR-13           2 03-MAR-13 09-MAR-13
01-MAR-13           3 10-MAR-13 16-MAR-13
01-MAR-13           4 17-MAR-13 23-MAR-13
01-MAR-13           5 24-MAR-13 30-MAR-13
01-MAR-13           6 31-MAR-13 31-MAR-13
01-APR-13           1 01-APR-13 06-APR-13
01-APR-13           2 07-APR-13 13-APR-13
01-APR-13           3 14-APR-13 20-APR-13
01-APR-13           4 21-APR-13 27-APR-13
01-APR-13           5 28-APR-13 30-APR-13
01-MAY-13           1 01-MAY-13 04-MAY-13
01-MAY-13           2 05-MAY-13 11-MAY-13
01-MAY-13           3 12-MAY-13 18-MAY-13
01-MAY-13           4 19-MAY-13 25-MAY-13
01-MAY-13           5 26-MAY-13 31-MAY-13
...
01-DEC-13           5 29-DEC-13 31-DEC-13

```

62 rows selected.

62 weeks in the year.

We probably need to account for more than just 2013. The following modification will handle 20 years starting with the year 2000: SELECT
MONTH_START,
WN.WEEK_NUMBER,
DECODE(WEEK_NUMBER,1,MONTH_START,START_SECOND_WEEK + (WN.WEEK_NUMBER-2)*7) START_WEEK,   DECODE(SIGN(MONTH_END-(START_SECOND_WEEK + (WN.WEEK_NUMBER-1)*7)),-1,MONTH_END,START_SECOND_WEEK + (WN.WEEK_NUMBER-1)*7 - 1) END_WEEK FROM
(SELECT
DUAL
CONNECT BY
LEVEL<=(12*20)) MONTHS,
(SELECT
LEVEL WEEK_NUMBER
FROM
DUAL
CONNECT BY
LEVEL<=6) WN
WHERE
(START_SECOND_WEEK + (WN.WEEK_NUMBER-2)*7)<=MONTHS.MONTH_END;

If you were to use the above approach, the next step would be to slide the above into an inline view and join it to your TRADING_SERVICE_STATS table using a BETWEEEN clause: DATE_SENT BETWEEN START_WEEK AND END_WEEK Charles Hooper
IT Manager/Oracle DBA
K&M Machine-Fabricating, Inc. Received on Wed May 01 2013 - 13:30:23 CEST

Original text of this message