Reputation: 78
So I'm counting activity records from users in my system. I get the activity counter for each day in a certain month and year, just like the query that follows
SELECT CONVERT(date, VIS_DATETIME) AS DATETIME, COUNT(*) AS ACTIVITY
FROM ACTIVITY
WHERE DATEPART(year, VIS_DATETIME) = 2016 AND
DATEPART(month, VIS_DATETIME) = 3
GROUP BY CONVERT(date, VIS_DATETIME)
ORDER BY CONVERT(date, VIS_DATETIME)
The question is, if, let's say, March 28th doesn't have any activity, it won't be even listed. But, for my charts API, I need to get that listed and with a 0
for the counter.
Obviously, accepting suggestions!
Upvotes: 2
Views: 5120
Reputation: 21
Similar to Arun's answer this will create a temp Table you can join on and save you creating a permanent table in database.
DECLARE @@startDate DATETIME = '2016-03-01';
DECLARE @@endDate DATETIME = '2016-03-31';
DECLARE @@tempCalendar TABLE (
[Id] INT IDENTITY(1,1) NOT NULL,
[Year] INT NOT NULL,
[Month] INT NOT NULL,
[Day] INT NOT NULL
);
DECLARE @@dateCount DATETIME = @@startDate;
WHILE (@@dateCount <= @@endDate)
BEGIN
INSERT INTO @@tempCalendar
VALUES (
DATEPART(YEAR, @@dateCount)
, DATEPART(MONTH, @@dateCount)
, DATEPART(DAY, @@dateCount)
);
SET @@dateCount = DATEADD(DAY, 1, @@dateCount);
END
SELECT c.[Year]
, c.[Month]
, c.[Day]
, COUNT(a.Id) AS ACTIVITY
FROM @@tempCalendar c
LEFT OUTER JOIN ACTIVITY a ON c.[Year] = DATEPART(YEAR, a.VIS_DATETIME)
AND c.[Month] = DATEPART(MONTH, a.VIS_DATETIME)
AND c.[Day] = DATEPART(DAY, a.VIS_DATETIME)
WHERE DATEPART(YEAR, a.VIS_DATETIME) = DATEPART(YEAR, @@startDate)
AND DATEPART(MONTH, a.VIS_DATETIME) = DATEPART(MONTH, @@startDate)
GROUP BY c.[Year], c.[Month], c.[Day]
ORDER BY c.[Year], c.[Month], c.[Day];
Upvotes: 0
Reputation: 1269743
Hmmm, if you want innovative. Here are two steps:
How do these work?
alter table activity add column ActivityIsValid smallint default 1;
That way, new rows go in as a valid activity, not invalid.
Then schedule a SQL Server agent job to run the following code once per day:
insert into activity(vis_datetime, ActivityIsValid)
values(cast(getdate() as date), 0);
Then you can run your query as:
SELECT CONVERT(date, VIS_DATETIME) AS DATETIME,
SUM(CASE WHEN ActivityIsValid = 1 THEN 1 ELSE 0 END) AS ACTIVITY
FROM ACTIVITY
WHERE VIS_DATETIME >= '2016-03-01' AND
VIS_DATETIME < '2016-04-01
GROUP BY CONVERT(date, VIS_DATETIME)
ORDER BY CONVERT(date, VIS_DATETIME);
Of course, this only works moving forward. You can manually insert the extra rows in the historical data.
Also note the change to the WHERE
clause. By doing direct date comparisons, the SQL engine can make use of an index.
Upvotes: 0
Reputation: 113
Declare @DayOfMonth TinyInt Set @DayOfMonth = 1
Declare @Month TinyInt Set @Month = 1
Declare @Year Integer Set @Year = 2016
Declare @startDate datetime
Declare @endDate datetime
-- ------------------------------------
Select @startDate = DateAdd(day, @DayOfMonth - 1,
DateAdd(month, @Month - 1,
DateAdd(Year, @Year-1900, 0)))
select @endDate = dateadd(month,1,@startDate)
;with dateRange as
(
select dt = dateadd(dd, 0, @startDate)
where dateadd(dd, 0, @startDate) < @endDate
union all
select dateadd(dd, 1, dt)
from dateRange
where dateadd(dd, 1, dt) < @endDate
)
select *
from dateRange
The above query gives all the dates in the month,you can left join it with your aggregated query to get the entries with zero count.
Upvotes: 4
Reputation: 16641
Create a table that contains all dates. Then do a left join with the Activity table. Group on the date, and do a COUNT
on Activity.id. The left join ensures that all dates from the date table are included in the result set, even if they are not matched in the join clause.
Upvotes: 4