查找连续几天的服务,中间没有休息日

时间:2014-10-15 21:39:31

标签: sql sql-server date

服务表:

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符号。

4 个答案:

答案 0 :(得分: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

union语句中的第二个查询是确定那些连续几天没有多个记录的服务记录的原因。

答案 1 :(得分:0)

在这里,我认为这是最简单的方法:

SELECT customerid, sum(datediff([serv-end-date],[serv-start-date]))
  FROM [service]
 GROUP BY customerid

您必须决定当天的开始/结束记录是否计为1.如果是,则将一个添加到datediff函数,例如sum(datediff([serv-end-date],[serv-start-date]) + 1)

如果您不想计算当天的服务但是想要在总结时计算开始/结束日期,则只需在开始和结束日期不同时添加一个+1的函数。如果您想了解如何做到这一点,请告诉我。

答案 2 :(得分:0)

我能想到解决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

确定每个客户的最长连续天数(或最新/最长时间,如果有平局)。这将允许您随后返回临时表以提取与该块相关的行 - 通过搜索customeridserviceenddate不是maxconsecutivedays )。不确定这适合您的使用案例 - 但它可能会有所帮助。

答案 3 :(得分:0)

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)