从连续范围中获取第一个和最后一个值

时间:2017-06-14 19:45:38

标签: sql sql-server sql-server-2008 gaps-and-islands

我希望获得每个enter_date的连续leave_dateenter_day值的第一个leave_day和最后一个id。鉴于此示例数据:

+-----+------------+------------+-----------+-----------+
| id  | enter_date | leave_date | enter_day | leave_day |
+-----+------------+------------+-----------+-----------+
| 111 | 2016-07-29 | 2016-12-01 |         1 |        75 |
| 111 | 2016-12-02 | 2017-01-13 |        76 |        95 |
| 111 | 2017-01-17 | 2017-06-02 |        96 |       181 |
| 222 | 2016-07-29 | 2016-12-02 |         1 |        76 |
| 222 | 2017-01-30 | 2017-06-02 |       105 |       181 |
| 333 | 2016-08-01 | 2017-06-02 |         1 |       180 |
+-----+------------+------------+-----------+-----------+

我想要以下结果:

+-----+------------+------------+
| id  | enter_date | leave_date |
+-----+------------+------------+
| 111 | 2016-07-29 | 2017-06-02 |
| 222 | 2016-07-29 | 2016-12-02 |
| 222 | 2017-01-30 | 2017-06-02 |
| 333 | 2016-08-01 | 2017-06-02 |
+-----+------------+------------+

我想要ID 111的一条记录,因为任何enter_day和之前的leave_day之间都没有差距。

我想要ID 222的两个记录,因为存在差距(第75到104天)。

编辑:到目前为止,我没有为ID 111提供正确的leave_date

with cte as (
    select a.id, a.enter_date, a.leave_date, b.enter_date next_ed, b.leave_date next_ld
    from #tbl a
    join #tbl b on b.id = a.id and b.enter_day = a.leave_day + 1
)
select id, min(enter_date) enter_date, max(leave_date) leave_date
from cte
group by id
union
select a.id, a.enter_date, a.leave_date
from #tbl a
left join #tbl b on b.id = a.id and b.enter_day = a.leave_day + 1
left join cte c on c.id = a.id and c.next_ed = a.enter_date and c.next_ld = a.leave_date
where b.id is null and c.id is null
order by 1,3

1 个答案:

答案 0 :(得分:0)

以下是范围的Gaps-and-Islands示例。

我使用了一个临时计数表,但实际的数字/计数也可以解决这个问题。

如果您只运行内部查询,您将很快看到6行样本数据如何分解为514行。然后是应用分组聚合以获得最终结果的一个小问题。

示例

Declare @YourTable Table ([id] int,[enter_date] date,[leave_date] date,[enter_day] int,[leave_day] int)
Insert Into @YourTable Values
 (111,'2016-07-29','2016-12-01',1,75)
,(111,'2016-12-02','2017-01-13',76,95)
,(111,'2017-01-17','2017-06-02',96,181)
,(222,'2016-07-29','2016-12-02',1,76)
,(222,'2017-01-30','2017-06-02',105,181)
,(333,'2016-08-01','2017-06-02',1,180)

Select ID
      ,[enter_date] = min([enter_date])
      ,[leave_date] = max([leave_date])
 From (
        Select *
              ,Grp = N - Row_Number() over (Partition By ID Order by N)
         From @YourTable A
         Join (
                  Select Top (Select max([leave_day]-[enter_day])+1 From @YourTable)
                         N=-1+Row_Number() Over (Order By (Select Null))
                  From  master..spt_values n1,master..spt_values n2
              ) B on B.N between [enter_day] and [leave_day]
      ) A 
 Group By [ID],Grp
 Order By [ID],min([enter_date])

返回

ID  enter_date  leave_date
111 2016-07-29  2017-06-02
222 2016-07-29  2016-12-02
222 2017-01-30  2017-06-02
333 2016-08-01  2017-06-02