I have the following query, it is working fine to show the cricket time played per day. All I need is to show 0 when no cricket is played. At the moment it is skipping those
This is a long query but should give you what you need.. It will also work for multiple activities.
;
WITH CTE
AS ( SELECT email ,
last_update ,
activity ,
starttime ,
endtime ,
duration AS [Totaltime]
FROM users
WHERE activity = 'cricket'
AND email = 'abc'
GROUP BY email ,
activity ,
duration ,
starttime ,
endtime ,
last_update
),
cteSummary
AS ( SELECT activity ,
CAST(starttime AS DATE) AS date ,
SUM(DATEDIFF(second, starttime, endtime)) / 60.0 AS TimePerDay
FROM cte
WHERE starttime >= DATEADD(day, -15, last_update)
GROUP BY activity ,
CAST(starttime AS DATE)
),
cteDateRange
AS ( SELECT activity ,
MIN(date) AS MinDate ,
MAX(date) AS MaxDate
FROM cteSummary
GROUP BY activity
),
cteDateRecur
AS ( SELECT activity ,
CalDate = CONVERT(DATE, MinDate) ,
MaxDate
FROM cteDateRange
UNION ALL
SELECT activity ,
CalDate = DATEADD(DAY, 1, c.CalDate) ,
MaxDate
FROM cteDateRecur c
WHERE c.CalDate < c.MaxDate
)
SELECT d.activity ,
d.CalDate AS date ,
ISNULL(s.TimePerDay, 0) AS TimePerDay
FROM cteDateRecur d
LEFT JOIN cteSummary s ON d.CalDate = s.date
OPTION ( MAXRECURSION 0 )
You need a list of dates. The easiest way -- if you have data on each date, but just not for the where
conditions -- is to use conditional aggregation:
Select activity, cast(starttime as date) as date,
SUM(case when activity = 'cricket' and email = 'abc'
then datediff(second, starttime, endtime))/60.0
else 0
end) as TimePerDay
from users
where starttime >= dateadd(day, -15, last_update)
group by activity, cast(starttime as date);
Otherwise, if the dates are not in the table, then you need a list of dates. This can come from:
Try this. You can generate the missing dates in a union by using the existing CTE as a basis for Row_Number()
;WITH CTE AS (
SELECT email, last_update, activity, starttime, endtime, duration as [Totaltime] from users
WHERE activity='cricket' and email='abc'
GROUP BY email, activity, duration, starttime, endtime, last_update
)
Select activity, cast(starttime as date) as date,
SUM(datediff(second, starttime, endtime))/60.0 as TimePerDay
from cte
where starttime >= dateadd(day, -15, last_update)
group by activity, cast(starttime as date)
UNION ALL
Select 'cricket' activity, everyday.[date], 0
FROM
(
select top 1000 dateadd(day,row_number() OVER (order by starttime),(select cast(min(starttime) as date) from CTE)) [date]
from CTE
) everyday
WHERE everyday.[date] NOT IN (SELECT cast(starttime as date) FROM CTE)
AND everyday.[date] < (SELECT cast(max(starttime) as date) from CTE)
ORDER BY date
First You have to generate the Date series with a query like this:
-- Generate 15 past days name from now
set @n:=date(now() + interval 1 day);
select (select @n:= @n - interval 1 day) day_series from users limit 15;
and then join select with date_series. for null values you can use COALESCE:
SELECT q2.day_series as days , COALESCE(TimePerDay, 0) as TPD from Your_select q1
right join (
-- select date_series
) as q2
on q1.Timestamp = q2.day_series and ....
--- final Query based on my understanding from 1st query
set @n:=date(now() + interval 1 day);
SELECT activity
,cast(starttime AS DATE) AS q1_DATE
,COALESCE(SUM(datediff(second, starttime, endtime)) / 60.0, 0) AS TimePerDay
FROM (
SELECT email
,last_update
,activity
,starttime
,endtime
,duration AS Totaltime
FROM users
WHERE activity = 'cricket'
AND email = 'abc'
GROUP BY email
,activity
,duration
,starttime
,endtime
,last_update
) q1
RIGHT JOIN (
SELECT (
SELECT @n: = @n - interval 1 day
) day_series
FROM users limit 15
) q2 ON q1.q1_DATE = q2.day_series
WHERE starttime >= dateadd(day, - 15, last_update)
GROUP BY activity
,cast(starttime AS DATE);
you could try this also :- (this could only for one particular activity)
Set Nocount On;
Declare @MinDate Date
,@MaxDate Date
Declare @test Table
(
activity Varchar(100)
,date Date
,TimePerDay Decimal(5,2)
)
Declare @result Table
(
activity Varchar(100)
,date Date
,TimePerDay Decimal(5,2) Default 0
)
;WITH CTE AS
(
SELECT email
,last_update
,activity
,starttime
,endtime
,duration As Totaltime
From users With (Nolock)
WHERE activity ='cricket'
And email = 'abc'
GROUP BY email
,activity
,duration
,starttime
,endtime
,last_update
)
Insert Into @test(activity,date,TimePerDay)
Select activity
,Cast(starttime as date) As date
,SUM(datediff(second, starttime, endtime))/60.0 As TimePerDay
From cte With (Nolock)
where starttime >= dateadd(day, -15, last_update)
group by activity
,cast(starttime as date)
Select @MinDate = Min(Date)
,@MaxDate = Max(Date)
From @test
;With AllDates As
(
Select @MinDate As xDate
From @test As t1
Where t1.date = @MinDate
Union All
Select Dateadd(Day, 1, xDate)
From AllDates As ad
Where ad.xDate < @MaxDate
)
One way is :- (left join)
Select 'cricket' As activity
,ad.xDate
,Isnull(t.TimePerDay,0) As TimePerDay
From AllDates As ad With (Nolock)
Left Join @test As t On ad.xDate = t.date
another way is :- (insert with all dates and update)
Insert Into @result(activity,date)
Select 'cricket'
,ad.xDate
From AllDates As ad With (Nolock)
Update t
Set t.TimePerDay = t1.TimePerDay
From @result As t
Join @test As t1 On t.date = t1.date
Select *
From @result As r
output
Update
Declare @MinDate Date
,@MaxDate Date
Select @MaxDate = Getdate()
,@MinDate = Dateadd(Day, -14, @MaxDate)
;With AllDates As
(
Select @MinDate As xDate
Union All
Select Dateadd(Day, 1, xDate)
From AllDates As ad
Where ad.xDate < @MaxDate
)
Select @activity As activity ---- @activity (your stored procedure parameter)
,ad.xDate
,Isnull(t.TimePerDay,0) As TimePerDay
From AllDates As ad With (Nolock)
Left Join @test As t On ad.xDate = t.date
AS PREPARATION, you need to generate a list of dates. There are multiple ways for that. One is a recursive CTE. For example, this one creates a list of the last 15 days:
with datelist ([date]) as
(
select dateadd(dd, -15, cast(getdate() as date)) as [date] -- 15 days back
union all
select dateadd(dd, 1, [date]) from datelist where dateadd(dd, 1, [date]) <= getdate()
)
select * from datelist option (maxrecursion 400)
FOR THE FINAL SOLUTION, you now need to create a LEFT JOIN
between the datelist
and with a subquery with your user
table.
An overall solution for your question therefore goes as follows.
I have also put everything together in a SQLFiddle: http://sqlfiddle.com/#!3/36510/1
with datelist ([date]) as
(
select dateadd(dd, -15, cast(getdate() as date)) as [date] -- 15 days back
union all
select dateadd(dd, 1, [date]) from datelist where dateadd(dd, 1, [date]) <= getdate()
)
select 'cricket' as activity,
d.[date],
coalesce(SUM(datediff(second, u.starttime, u.endtime)/60.0), 0)
as TimePerDay
from datelist d
left join
(
select [starttime], [endtime], cast(starttime as date) as [date]
from [users]
where activity='cricket' and email='abc'
) u
on d.[date] = u.[date]
group by d.[date]
option (maxrecursion 400)
So this query will give you the table for the cricket activity of the last 15 days of one specific user.