查找连续的服务天数,中间没有休息日
Posted
技术标签:
【中文标题】查找连续的服务天数,中间没有休息日【英文标题】:Find consecutive days of service without a day break in between 【发布时间】:2014-10-15 21:39:31 【问题描述】:服务表:
claimid, customerid, serv-start-date, service-end-date, charge
1, A1, 1-1-14 , 1-5-14 , $200
2, A1, 1-6-14 , 1-8-14 , $300
3, A1, 2-1-14 , 2-1-14 , $100
4, A2, 2-1-14 , 2-1-14 , $100
5, A2, 2-3-14 , 2-5-14 , $100
6, A2, 2-6-14 , 2-8-14 , $100
问题:
基本上看最大总连续天数 服务开始日期和结束日期。
客户 A1 为 8 天(1-5 加 6-8),客户 A2 为 5 6 天(3-5 加 6-8)...(claimid 是唯一的 PK )。
日期采用 m-d-yy 表示法。
【问题讨论】:
如果它是每个客户的单一值,那么您可能意味着找到最大连续服务天数。而对于客户 2,它实际上应该是 6。 您能否有一个客户的服务记录具有重叠的日期范围。为便于讨论,数据库/表记录客户 A3 在 2014-01-01 至 2014-01-05 和 2014-01-03 至 2014-01-08 有服务是否合法? 在其他相关问题中,请查看Date difference between consecutive rows、Aggregate adjacent only records with T-SQL 和Analytical Query。 【参考方案1】:这有点混乱,因为您可能有没有多条记录的客户。这使用公共表表达式以及 max
聚合和 union all
来确定您的结果:
with cte as (
select s.customerid,
s.servicestartdate,
s2.serviceenddate,
datediff(day,s.servicestartdate,s2.serviceenddate)+1 daysdiff
from service s
join service s2 on s.customerid = s2.customerid
and s2.servicestartdate in (s.serviceenddate, dateadd(day,1,s.serviceenddate))
)
select customerid, max(daysdiff) daysdiff
from cte
group by customerid
union all
select customerid, max(datediff(day, servicestartdate, serviceenddate))
from service s
where not exists (
select 1
from cte
where s.customerid = cte.customerid
)
group by customerid
SQL Fiddle Demo
union
语句中的第二个查询是确定那些连续几天没有多条记录的服务记录。
【讨论】:
这是否处理看似合理但未说明的情况,即单个客户连续 3 次(或更多)提供服务:例如,2014-01-01 到 2014-01-03 和 2014-01- 04 到 2014-01-06 和 2014-01-07 到 2014-01-07(应该产生 7 天的答案)? @JonathanLeffler -- 我同意,这会变得更加混乱 :) 在我的脑海中,也许是一个带有交叉连接的日期表以获得所需的结果。递归 CTE 可能——但无论如何它都不是微不足道的...... @sgeddes ya 如果记录都按照样本的方式排序,这很好,但很可能不是。所以它不会捕捉到我们需要的东西。【参考方案2】:来吧,我认为这是最简单的方法:
SELECT customerid, sum(datediff([serv-end-date],[serv-start-date]))
FROM [service]
GROUP BY customerid
您必须决定同一天的开始/结束记录是否计为 1。如果是,则在 datediff 函数中添加 1,例如sum(datediff([serv-end-date],[serv-start-date]) + 1)
如果您不想计算同一天的服务,但希望在汇总时包括开始/结束日期,则需要添加一个仅在开始日期和结束日期不同时执行 +1 的函数.如果你想知道如何做到这一点,请告诉我。
【讨论】:
我看不出这是如何对连续天数求和的——它只是对每个客户的所有日期差异求和。 每条记录存储一组从开始日期到结束日期的连续天数。您所要做的就是按客户总结。【参考方案3】:我认为解决 Jonathan Leffler 描述的问题的唯一方法(在对另一个答案的评论中)是使用临时表来合并连续的日期范围。这最好在 SP 中完成 - 但如果以下批次无法产生您正在寻找的输出:-
select *, datediff(day,servicestartdate,serviceenddate)+1 as numberofdays
into #t
from service
while @@rowcount>0 begin
update t1 set
t1.serviceenddate=t2.serviceenddate,
t1.numberofdays=datediff(day,t1.servicestartdate,t2.serviceenddate)+1
from #t t1
join #t t2 on t2.customerid=t1.customerid
and t2.servicestartdate=dateadd(day,1,t1.serviceenddate)
end
select
customerid,
max(numberofdays) as maxconsecutivedays
from #t
group by customerid
临时表的更新需要循环进行,因为日期范围(我假设)可以分布在任意数量的记录(1->n)上。有趣的问题。
我已对代码进行了更新,以便临时表以一个额外的列结束,该列包含每条记录的日期范围内的天数。这允许以下操作:-
select x.customerid, x.maxconsecutivedays, max(x.serviceenddate) as serviceenddate
from (
select t1.customerid, t1.maxconsecutivedays, t2.serviceenddate
from (
select
customerid,
max(numberofdays) as maxconsecutivedays
from #t
group by customerid
) t1
join #t t2 on t2.customerid=t1.customerid and t2.numberofdays=t1.maxconsecutivedays
) x
group by x.customerid, x.maxconsecutivedays
识别每个客户的最长连续天数(或最新/最长,如果有平局)。这将允许您随后潜入临时表以提取与该块相关的行 - 通过搜索 customerid
和 serviceenddate
(不是 maxconsecutivedays)。不确定这是否适合您的用例 - 但它可能会有所帮助。
【讨论】:
很好的是有办法标记每条记录 1,2,3 以便我们知道哪些记录相互连接......所以对于 Cust A1 选择了哪些 Claim id 并枚举它们。 连续日期范围块中的索赔记录的serviceenddate
都将是相同的(在#t 中),因此应该可以设计一个select .... over(partition by ...)
来创建您的标签正在描述。当客户有两个或多个连续天数相同的日期范围块时,就会出现问题。您要选择哪个区块?
如果客户的连续天数相同,则以较大者为准【参考方案4】:
WITH chain_builder AS
(
SELECT ROW_NUMBER() OVER(ORDER BY s.customerid, s.CLAIMID) as chain_ID,
s.customerid,
s.serv-start-date, s.service-end-date, s.CLAIMID, 1 as chain_count
FROM services s
WHERE s.serv-start-date <> ALL
(
SELECT DATEADD(d, 1, s2.service-end-date)
FROM services s2
)
UNION ALL
SELECT chain_ID, s.customerid, s.serv-start-date, s.service-end-date,
s.CLAIMID, chain_count + 1
FROM services s
JOIN chain_builder as c
ON s.customerid = c.customerid AND
s.serv-start-date = DATEADD(d, 1, c.service-end-date)
),
chains AS
(
SELECT chain_ID, customerid, serv-start-date, service-end-date,
CLAIMID, chain_count
FROM chain_builder
),
diff AS
(
SELECT c.chain_ID, c.customerid, c.serv-start-date, c.service-end-date,
c.CLAIMID, c.chain_count,
datediff(day,c.serv-start-date,c.service-end-date)+1 daysdiff
FROM chains c
),
diff_sum AS
(
SELECT chain_ID, customerid, serv-start-date, service-end-date,
CLAIMID, chain_count,
SUM(daysdiff) OVER (PARTITION BY chain_ID) as total_diff
FROM diff
),
diff_comp AS
(
SELECT chain_ID, customerid,
MAX(total_diff) OVER (PARTITION BY customerid) as total_diff
FROM diff_sum
)
SELECT DISTINCT ds.CLAIMID, ds.customerid, ds.serv-start-date,
ds.service-end-date, ds.total_diff as total_days, ds.chain_count
FROM diff_sum ds
JOIN diff_comp dc
ON ds.chain_ID = dc.chain_ID AND ds.customerid = dc.customerid
AND ds.total_diff = dc.total_diff
ORDER BY customerid, chain_count
OPTION (maxrecursion 0)
【讨论】:
以上是关于查找连续的服务天数,中间没有休息日的主要内容,如果未能解决你的问题,请参考以下文章