【问题标题】:Return first and last date of contiguous blocks in intermittent date series返回间歇日期系列中连续块的第一个和最后一个日期
【发布时间】:2014-05-06 00:14:18
【问题描述】:

我正在使用 SQL Server 2008 R2 并有下表。

CREATE TABLE room
(id int, 
 dt date, 
 course char(1), 
 constraint pk_roomtime primary key (id, dt)
);

INSERT INTO room (id, dt, course) values
(1, '20140412', 'A'),
(1, '20140414', 'A'),
(1, '20140415', 'A'),
(1, '20140416', 'B'),
(1, '20140417', 'A'),
(1, '20140421', 'A'),
(1, '20140422', 'B'),
(1, '20140423', 'B');

我必须找到房间中每个课程块的第一个和最后一个日期,而不考虑日期之间的间隔。我的房间 1 的结果应该是这样的:

A    20140412    20140415
B    20140416    20140416
A    20140417    20140421
B    20140422    20140423

我尝试使用连接到日历表,但无法正常工作。

【问题讨论】:

  • 课程 A 的前三个日期(12 / 14 / 15)不连续。那么课程 A 的两个后续日期(17 / 21)不属于课程 A 的第一个结果集的规则是什么?
  • 说实话,在 2008 年它是迄今为止最容易使用光标的。如果您可以升级到 2012,则可以通过改进的窗口功能很好地解决它。
  • @dean - 您将如何获得与窗口函数中提到的结果类似的结果?我看不出前三个数据集与第 5 个和第 6 个数据集之间有什么区别来获得不同的结果 - 除了 B 类中断。这是分隔集合的规则吗?
  • @Nico 在下面查看我的答案以了解 2012 年的解决方案。我无法将其粘贴到 cmets 中。

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


【解决方案1】:

为了完整起见,并回答@Nico 评论中提出的问题,这是 2012 年的解决方案(我知道 OP 使用 2008r2):

with x as (
    select *,
    lag(dt) over(order by dt) as lag_dt,
    lag(course) over(order by dt) as lag_course
    from room
),
y as (
    select *,
    sum(case when course<>lag_course then 1 else 0 end) over(order by dt) as grp
    from x
)
select min(course), min(dt), max(dt)
from y
group by grp

【讨论】:

    【解决方案2】:

    不完全是您要求的结果集,但我认为结果本身符合您的想法:

    with "cte" as 
    (
    select 
        rn = row_number() over ( order by dt )
        , * 
    from room
    )
    ,"preprocessed" as 
    (
    select
        "current"."course"
        , startDate = case when "predecessor"."id" is null then "current"."dt" else null end
        , endDate = case when "successor"."id" is null then "current"."dt" else null end
        , "current"."dt"
    from 
        "cte" as "current"
        left join "cte" as predecessor
            on 1 = 1
            and "current"."id" = predecessor."id"
            and "current"."course" = predecessor."course"
            and "current"."rn" -1 = predecessor."rn"
    
        left join "cte" as successor
            on 1 = 1
            and "current"."id" = successor."id"
            and "current"."course" = successor."course"
            and "current"."rn" +1 = successor."rn"
    ), "pvt" as
    (
    select
        "course"
        , "dt"
        , "type"
    from
        ( select "course", "startDate", "endDate"  from "preprocessed" ) as data
         unpivot ( "dt" for "type" in ( "startDate", "endDate")) as pvt
    )
    select * from 
        "pvt"
    

    See SQL-Fiddle.

    【讨论】:

      猜你喜欢
      • 2011-07-17
      • 2018-06-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多