How to extend the query to add 0 in the cell when no activity is performed

后端 未结 7 1558
甜味超标
甜味超标 2020-12-21 09:57

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

相关标签:
7条回答
  • 2020-12-21 10:11

    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 )
    
    0 讨论(0)
  • 2020-12-21 10:12

    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:

    • A calendar table.
    • A recursive CTE.
    • An explicit list in the query.
    0 讨论(0)
  • 2020-12-21 10:19

    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
    
    0 讨论(0)
  • 2020-12-21 10:23

    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);
    
    0 讨论(0)
  • 2020-12-21 10:31

    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

    enter image description here

    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
    
    0 讨论(0)
  • 2020-12-21 10:31

    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.

    0 讨论(0)
提交回复
热议问题