2017-03-29 125 views
1

我有一個表中包含的每個時間的寄存器中的SQL Server數據庫的用戶從我的應用程序下載的圖像,所以我的表TBL_Downloads具有以下結構:計數寄存器,包括零

UserID| ImageID | DownloadDate    | 
------+-----------+---------------------------+ 
    292 | 782 | 02-01-2016 14:20:22.737 | 
    292 | 783 | 02-01-2016 14:20:22.737 | 
    292 | 784 | 02-02-2016 14:20:22.737 | 
    292 | 785 | 02-04-2016 14:20:22.737 | 
    292 | 786 | 02-05-2016 14:20:22.737 | 
    292 | 787 | 02-06-2016 14:20:22.737 | 

在表僅僅爲了簡化示例,僅示出了針對1個特定用戶的寄存器。

我想要的是一個結果表,其中包含特定用戶在過去30天內每天下載的下載次數,包括沒有下載的日期爲零。我目前有以下查詢:

SELECT COUNT(*) AS Downloads 
FROM TBL_Downloads 
WHERE DownloadDate BETWEEN DATEADD(day, -30, GETDATE()) AND GETDATE() 
    AND IdUser = 292 
GROUP BY CAST(DownloadDate AS DATE) 

這將返回一個表的總和,但僅適用於至少有一個下載條目的天。

你有什麼想法,我該如何解決這個問題?

回答

2

您可以使用日曆或日期表來處理這類事情。

對於內存只有152KB,可以有30個年份的日期的表中的這個:

/* dates table */ 
declare @fromdate date = '20000101'; 
declare @years int = 30; 
/* 30 years, 19 used data pages ~152kb in memory, ~264kb on disk */ 
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n)) 
select top (datediff(day, @fromdate,dateadd(year,@years,@fromdate))) 
    [Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,@fromdate)) 
into dbo.Dates 
from n as deka cross join n as hecto cross join n as kilo 
       cross join n as tenK cross join n as hundredK 
order by [Date]; 
create unique clustered index ix_dbo_Dates_date 
    on dbo.Dates([Date]); 

如果不考慮創建一個表的實際步驟中,您可以只用這一個common table expression內使用:

declare @fromdate date = dateadd(day , datediff(day , 0, getdate())-30 , 0); 
declare @thrudate date = dateadd(day , datediff(day , 0, getdate()), 0); 
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n)) 
, dates as (
    select top (datediff(day, @fromdate, @thrudate)+1) 
     [Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,@fromdate)) 
    from n as deka cross join n as hecto cross join n as kilo 
       cross join n as tenK cross join n as hundredK 
    order by [Date] 
) 
select [Date] 
from dates; 

二者必選其一,像這樣:

select 
    d.Date 
    , count(t.DownloadDate) as DownloadCount 
from dates d 
    left join TBL_Downloads t 
    on d.date = convert(date,t.DownloadDate) 
    and t.userid = 292 
where d.date >= dateadd(day , datediff(day , 0, getdate())-30 , 0) 
    and d.date <= dateadd(day , datediff(day , 0, getdate()), 0) 
group by d.date 

rextester 演示http://rextester.com/ISK37732(日期變更爲內最後的30天)

回報:

+------------+---------------+ 
| Date | DownloadCount | 
+------------+---------------+ 
| 2017-02-27 |    0 | 
| 2017-02-28 |    0 | 
| 2017-03-01 |    2 | 
| 2017-03-02 |    1 | 
| 2017-03-03 |    0 | 
| 2017-03-04 |    1 | 
| 2017-03-05 |    1 | 
| 2017-03-06 |    1 | 
| 2017-03-07 |    0 | 
| 2017-03-08 |    0 | 
| 2017-03-09 |    0 | 
| 2017-03-10 |    0 | 
| 2017-03-11 |    0 | 
| 2017-03-12 |    0 | 
| 2017-03-13 |    0 | 
| 2017-03-14 |    0 | 
| 2017-03-15 |    0 | 
| 2017-03-16 |    0 | 
| 2017-03-17 |    0 | 
| 2017-03-18 |    0 | 
| 2017-03-19 |    0 | 
| 2017-03-20 |    0 | 
| 2017-03-21 |    0 | 
| 2017-03-22 |    0 | 
| 2017-03-23 |    0 | 
| 2017-03-24 |    0 | 
| 2017-03-25 |    0 | 
| 2017-03-26 |    0 | 
| 2017-03-27 |    0 | 
| 2017-03-28 |    0 | 
| 2017-03-29 |    0 | 
+------------+---------------+ 

號碼和日曆表參考:

+0

該解決方案是偉大的,但它不考慮TBL_Downloads表具有來自不同用戶的寄存器,一旦我添加'WHERE userid = 292',cero中的行就會再次消失。或者可能我做錯了 –

+0

@AlejandroFlores在'left join'條件中放置'和userid = 292'而不是'where' - 更新了我的答案和rextester演示以展示我的意思。 – SqlZim

+0

我以前使用過同樣的條款。我不知道爲什麼我不提出這個問題。它工作完美。我對此有點新,非常感謝。 –

0

您可以使用組通過使用通用表表達式

--Final Query - For this query to execute you require #dates to be populated as below 
select cte_start_date as [date], coalesce(cnt,0) as DownLoadCount from #dates d left join 
(
    select userid,convert(date,DownloadDate) dt , count(convert(date,DownloadDate)) as cnt from #yourDownloads 
    group by userid, convert(date,DownloadDate) 
) a 
on d.cte_start_date = a.dt 

公用表表達式的遞歸查詢來填充日期爲當月找到計數和生成日期

--Populuating dates 
declare @start_date date = '02-01-2016' 
declare @end_date date = eomonth('02-01-2016') 
;WITH CTE AS 
(
    SELECT @start_date AS cte_start_date 
    UNION ALL 
    SELECT DATEADD(DAY, 1, cte_start_date) 
    FROM CTE 
    WHERE DATEADD(DAY, 1, cte_start_date) <= @end_date  
) 
select * into #dates from cte 

你輸入表

create table #yourDownloads (UserId int, ImageId int, DownloadDate datetime) 

insert into #yourDownloads 
(UserID , ImageID , DownloadDate ) values 
( 292 , 782 ,'02-01-2016 14:20:22.737') 
,( 292 , 783 ,'02-01-2016 14:20:22.737') 
,( 292 , 784 ,'02-02-2016 14:20:22.737') 
,( 292 , 785 ,'02-04-2016 14:20:22.737') 
,( 292 , 786 ,'02-05-2016 14:20:22.737') 
,( 292 , 787 ,'02-06-2016 14:20:22.737') 
+0

您可能想要查看遞歸cte與其他一些選項相比的性能。它會變得很糟糕,因爲你需要生成更多的值。[生成一個沒有循環的集合或序列 - 2 - Aaron Bertrand](https://sqlperformance.com/2013/01/t-sql-queries/generate-a-set -2) – SqlZim