I have a MySQL requirement to select data from a table based on a start date and end date and group it by weekly also selecting the data in reverse order by date. Assume that, I have chosen the start date as 1st November and the end date as 04 December.
Now, I would like to fetch the data as 04 December to 28 November, 27 November to 20 November, 19 November to 12 November
and so on and sum the value count for that week.
Given an example table,
id | value | created_at |
---|---|---|
1 | 10 | 2021-10-11 |
2 | 13 | 2021-10-17 |
3 | 11 | 2021-10-25 |
4 | 8 | 2021-11-01 |
5 | 1 | 2021-11-10 |
6 | 4 | 2021-11-18 |
7 | 34 | 2021-11-25 |
8 | 17 | 2021-12-04 |
Now the result should be like 2021-12-04 to 2021-11-28 as one week, following the same in reverse order and summing the column value data for that week. I have tried in the query to add the interval of 7 days after the end date but it didn't work.
SELECT count(value) AS total, MIN(R.created_at)
FROM data_table AS D
WHERE D.created_at BETWEEN '2021-11-01' AND '2021-12-04' - INTERVAL 7 DAY ORDER BY D.created_at;
And it's also possible to have the last week may have lesser than 7 days.
Expected output:
end_interval | start_interval | total |
---|---|---|
2021-12-04 | 2021-11-27 | 17 |
2021-11-27 | 2021-11-20 | 34 |
2021-11-20 | 2021-11-13 | 4 |
2021-11-13 | 2021-11-06 | 1 |
2021-11-06 | 2021-10-30 | 8 |
2021-10-30 | 2021-10-25 | 11 |
Note that the last week is only 5 days depending upon the selected from and end dates.
CodePudding user response:
One option to address this problem is to
- generate a calendar of all your intervals, beginning from last date till first date, with a split of your choice, using a recursive query
- joining back the calendar with the original table
- capping start_interval at your start_date value
- aggregating values for each interval
You can have three variables to be set, to customize your date intervals and position:
SET @start_date = DATE('2021-10-25');
SET @end_date = DATE('2021-12-04');
SET @interval_days = 7;
Then use the following query, as already described:
WITH RECURSIVE cte AS (
SELECT @end_date AS end_interval,
DATE_SUB(@end_date, INTERVAL @interval_days DAY) AS start_interval
UNION ALL
SELECT start_interval AS end_interval,
GREATEST(DATE(@start_date), DATE_SUB(start_interval, INTERVAL @interval_days DAY)) AS start_interval
FROM cte
WHERE start_interval > @start_date
)
SELECT end_interval, start_interval, SUM(_value) AS total
FROM cte
LEFT JOIN tab
ON tab.created_at BETWEEN start_interval AND end_interval
GROUP BY end_interval, start_interval
Check the demo here.