【发布时间】:2018-09-05 14:46:03
【问题描述】:
最初的问题
鉴于以下数据集与日期表配对:
MembershipId | ValidFromDate | ValidToDate
==========================================
0001 | 1997-01-01 | 2006-05-09
0002 | 1997-01-01 | 2017-05-12
0003 | 2005-06-02 | 2009-02-07
在任何给定的日期或时间序列中,有多少 Memberships 处于打开状态?
初步回答
在here 提出这个问题后,这个答案提供了必要的功能:
select d.[Date]
,count(m.MembershipID) as MembershipCount
from DIM.[Date] as d
left join Memberships as m
on(d.[Date] between m.ValidFromDateKey and m.ValidToDateKey)
where d.CalendarYear = 2016
group by d.[Date]
order by d.[Date];
尽管评论者评论说 当非等值连接耗时太长时,还有其他方法。
跟进
因此,复制上述查询的输出时,仅 equijoin 的逻辑是什么样的?
目前的进展
根据目前提供的答案,我得出以下结论,在我使用的 320 万条Membership 记录中,它的性能优于硬件:
declare @s date = '20160101';
declare @e date = getdate();
with s as
(
select d.[Date] as d
,count(s.MembershipID) as s
from dbo.Dates as d
join dbo.Memberships as s
on d.[Date] = s.ValidFromDateKey
group by d.[Date]
)
,e as
(
select d.[Date] as d
,count(e.MembershipID) as e
from dbo.Dates as d
join dbo.Memberships as e
on d.[Date] = e.ValidToDateKey
group by d.[Date]
),c as
(
select isnull(s.d,e.d) as d
,sum(isnull(s.s,0) - isnull(e.e,0)) over (order by isnull(s.d,e.d)) as c
from s
full join e
on s.d = e.d
)
select d.[Date]
,c.c
from dbo.Dates as d
left join c
on d.[Date] = c.d
where d.[Date] between @s and @e
order by d.[Date]
;
从那以后,为了每天将这个聚合分成组成组,我有以下,它也表现良好:
declare @s date = '20160101';
declare @e date = getdate();
with s as
(
select d.[Date] as d
,s.MembershipGrouping as g
,count(s.MembershipID) as s
from dbo.Dates as d
join dbo.Memberships as s
on d.[Date] = s.ValidFromDateKey
group by d.[Date]
,s.MembershipGrouping
)
,e as
(
select d.[Date] as d
,e..MembershipGrouping as g
,count(e.MembershipID) as e
from dbo.Dates as d
join dbo.Memberships as e
on d.[Date] = e.ValidToDateKey
group by d.[Date]
,e.MembershipGrouping
),c as
(
select isnull(s.d,e.d) as d
,isnull(s.g,e.g) as g
,sum(isnull(s.s,0) - isnull(e.e,0)) over (partition by isnull(s.g,e.g) order by isnull(s.d,e.d)) as c
from s
full join e
on s.d = e.d
and s.g = e.g
)
select d.[Date]
,c.g
,c.c
from dbo.Dates as d
left join c
on d.[Date] = c.d
where d.[Date] between @s and @e
order by d.[Date]
,c.g
;
任何人都可以改进上述内容吗?
【问题讨论】:
-
你的方式并不是唯一的方式。我正在尝试其他几种方法来做到这一点。
-
@AlanBurstein 发现了什么值得注意的东西?
-
您的查询返回的计数不正确。运行查询,然后运行一个更简单的表单,该表单仅从成员资格表中计算单个日期,例如“2016-01-01”
-
@PittsburghDBA 您能否详细说明您指的是哪个查询而不是更正方式?尽管我很高兴看到我犯的任何错误,但我在测试中没有发现任何差异。
-
@PittsburghDBA 你是对的,会员资格的最后一天不计入当前查询。需要
dateadd(d,1,ValidToDateKey)来避免此问题。检查我的答案。
标签: sql sql-server tsql date join