Reputation: 787
If you have table like this:
Name | Data type |
---|---|
UserID | INT |
StartDate | DATETIME |
EndDate | DATETIME |
With data like this:
UserID | StartDate | EndDate |
---|---|---|
21 | 2021-01-02 00:00:00 | 2021-01-02 23:59:59 |
21 | 2021-01-03 00:00:00 | 2021-01-04 15:42:00 |
24 | 2021-01-02 00:00:00 | 2021-01-06 23:59:59 |
And you want to calculate number of users that is represented on each day in a week with a result like this:
Year | Week | NumberOfTimes |
---|---|---|
2021 | 1 | 8 |
2021 | 2 | 10 |
2021 | 3 | 4 |
Basically I want to to a Select like this:
SELECT YEAR(dateColumn) AS yearname, WEEK(dateColumn)as week name, COUNT(somecolumen)
GROUP BY YEAR(dateColumn) WEEK(dateColumn)
The problem I have is the start and end date if the date goes over several days I want it to counted each day. Preferably I don't want the same user counted twice each day. There are millions of rows that are constantly being deleted and added so speed is key.
The database is MS-SQL 2019
Upvotes: 0
Views: 1273
Reputation: 1271003
I would suggest a recursive CTE:
with cte as (
select userid, startdate, enddate
from t
union all
select userid, startdate,
enddate
from cte
where startdate < enddate and
week(startdate) <> week(enddate)
)
select year(startdate), week(startdate), count(*)
from cte
group by year(startdate), week(startdate)
option (maxrecursion 0);
The CTE expands the data by adding 7 days to each row. This should be one day per week.
There is a little logic in the second part to handle the situation where the enddate
ends in the same week as the last start date. The above solution assumes that the dates are all in the same year -- which seems quite reasonable given the sample data. There are other ways to prevent this problem.
Upvotes: 3
Reputation: 72415
You need to cross-join each row with the relevant dates.
Create a calendar table with columns of years and weeks, include a start and end date of the week. See here for an example of how to create one, and make sure you index those columns.
Then you can cross-join like this
SELECT
YEAR(dateColumn) AS yearname,
WEEK(dateColumn)as weekname,
COUNT(somecolumen)
FROM Table t
JOIN CalendarWeek c ON c.StartDate >= t.StartDate AND c.EndDate <= t.EndDate
GROUP BY YEAR(dateColumn), WEEK(dateColumn)
Upvotes: 0