SQLSERVER 2014在两个日期之间滚动n天的非重复计数

时间:2017-09-18 12:18:25

标签: sql-server

我有一张类似于下面的表格

UsedDate, ProductID, Count, usedBy
10/10/2017, Widgit1, 14, adr
10/10/2017, Widgit1, 20, mmg
10/10/2017, Widgit3, 5, mmg
11/10/2017, widgit2, 1, adr
11/10/2017, widgit1, 15, adr
11/10/2017, widgit2, 15, mmg

13/10/2017, widgit2, 8, adr
13/10/2017, widgit3, 5, adr
13/10/2017, widgit3, 4, mmg

现在,在两个日期(@startDate,@ endDate)之间的天数(@period)滚动期间我需要计算所使用的ProductID的不同计数,因此对于10/10它的2(widget1和widget 3)为11 / 10它的2(widget1,widget2)但滚动计数将是3以及期间的开始日期。但是有些日子可能会丢失,好像当天没有使用任何条目存储在数据库中。

通常,开始日期和结束日期相隔一年。

我意识到我可以通过

获得总数
SELECT
    Cast (dbo.Usage.UsedDate as Date) AS  UsedDate,  
    count (distinct dbo.Usage.ProductID) AS used
    FROM 
        dbo.Usage
    WHERE (dbo.Usage.UsedDate BETWEEN (@StartDate) AND (@EndDate))
    GROUP BY Cast (dbo.Usage.UsedDate as Date)

但是,如何对滚动日期范围进行分区并获得一个日期表,其中包含不同的产品总数?

所以如果startDate是10/10/2017 endDate是13/10/2017 期限为2天

这意味着第一行将是10/10/2017和下一行10/10/2017数据+ 11/10/2017数据 然而,第三行只是2017年10月13日的数据,因为它在下一个时期如果有一个12/10/2017那么它也将在那个新时期出现

 UsedDate, within_day_distinct_count, roll_distinct_count
  10/10/2017 2, 2
  11/10/2017 2, 3
  13/10/2017 2, 2

2 个答案:

答案 0 :(得分:1)

如果我理解你,那么所有期间都分为3部分:@startDate之前,@StartDate@EndDate之间,以及@EndDate之后。

在您的示例中,仅显示2个句点:之间和之后。

在每个运行不同计数的时间段都必须单独计算,因此以下是所有3个时段的union all代码:

declare @t table (UsedDate date, ProductID varchar(100), Cnt int, usedBy varchar(100));

insert into @t values

('20171010', 'Widgit1', 14, 'adr'),
('20171010', 'Widgit1', 20, 'mmg'),
('20171010', 'Widgit3', 5, 'mmg'),
('20171011', 'Widgit2', 1, 'adr'),
('20171011', 'Widgit1', 15, 'adr'),
('20171011', 'Widgit2', 15, 'mmg'),

('20171013', 'Widgit2', 8, 'adr'),
('20171013', 'Widgit3', 5, 'adr'),
('20171013', 'Widgit3', 4, 'mmg');

declare @startDate date = '20171010', @endDate date = '20171011'

select t.UsedDate,
       count(distinct t.ProductId) as within_day_distinct_count,
       count(distinct t1.ProductId) as roll_distinct_count
from @t t join @t t1
        on t.UsedDate >= t1.UsedDate 
WHERE t.UsedDate BETWEEN @StartDate AND @EndDate
and   t1.UsedDate BETWEEN @StartDate AND @EndDate
group by t.UsedDate

union all 

select t.UsedDate,
       count(distinct t.ProductId) as within_day_distinct_count,
       count(distinct t1.ProductId) as roll_distinct_count
from @t t join @t t1
        on t.UsedDate >= t1.UsedDate 
WHERE t.UsedDate > @EndDate
and   t1.UsedDate > @EndDate
group by t.UsedDate

union all 

select t.UsedDate,
       count(distinct t.ProductId) as within_day_distinct_count,
       count(distinct t1.ProductId) as roll_distinct_count
from @t t join @t t1
        on t.UsedDate >= t1.UsedDate 
WHERE t.UsedDate < @StartDate
and   t1.UsedDate < @StartDate
group by t.UsedDate;

答案 1 :(得分:0)

您可以使用Tally创建日期范围。

一个好的做法是为计数表创建一个视图。

DECLARE @Usage as table (UsedDate date , ProductID varchar(10) , Count int , usedBy varchar(5)) 
INSERT @Usage(UsedDate, ProductID, Count, usedBy) VALUES 
 ('20171010', 'Widgit1', 14, 'adr')
,('20171010', 'Widgit1', 20, 'mmg')
,('20171010', 'Widgit3', 5,  'mmg')
,('20171011', 'widgit2', 1,  'adr')
,('20171011', 'widgit1', 15, 'adr')
,('20171011', 'widgit2', 15, 'mmg')
,('20171013', 'widgit2', 8,  'adr')
,('20171013', 'widgit3', 5,  'adr')
,('20171013', 'widgit3', 4,  'mmg')


DECLARE @StartDate date = '20171010'
DECLARE @EndDate date  = '20171013'

;WITH lv0(N) AS (SELECT 0 FROM (VALUES (1),(1))G(N))
,lv1(N) AS (SELECT 0 FROM lv0 a CROSS JOIN lv0 b) -- 4
,lv2(N) AS (SELECT 0 FROM lv1 a CROSS JOIN lv1 b) -- 16
,lv3(N) AS (SELECT 0 FROM lv2 a CROSS JOIN lv2 b) -- 256
,lv4(N) AS (SELECT 0 FROM lv3 a CROSS JOIN lv3 b) -- 65,536
,lv5(N) AS (SELECT 0 FROM lv4 a CROSS JOIN lv4 b) -- 4,294,967,296
,cteTally(N) AS
(
SELECT  ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM lv5
)
,
DateRange AS ( 
SELECT DATEADD(day,N -1 ,@StartDate) DR 
FROM cteTally where N <= DATEDIFF(Day, @StartDate , @EndDate) + 1 
) 


,ProductByday AS (SELECT DISTINCT
    T.DR AS  UsedDate,  
    U.ProductID  used
    FROM 
    DateRange T
    LEFT JOIN @Usage U
    ON
    T.DR = Cast ( U.UsedDate as date)
  )

SELECT Distinct A.UsedDate , Count(Distinct B.used) used  from ProductByday A 
LEFT JOIN ProductByday B
ON A.UsedDate >= B.UsedDate
GROUP BY 
A.UsedDate

结果

UsedDate   used
---------- -----------
2017-10-10 2
2017-10-11 3
2017-10-12 3
2017-10-13 3