【问题标题】:SQL Server contiguous dates - summarizing multiple rows into contiguous start and end date rows without CTE's, loops,...sSQL Server 连续日期 - 将多行汇总为连续的开始和结束日期行,没有 CTE、循环、...
【发布时间】:2018-06-04 17:15:51
【问题描述】:

是否可以编写一个 sql 查询,将具有开始日期和结束日期的行汇总为具有连续开始日期和结束日期的行?

限制是它必须是常规 sql,即没有 CTE、循环等,因为使用了第三方工具,只允许 sql 语句以 Select 开头。

例如:

ID       StartDate         EndDate

1001,      Jan-1-2018,       Jan-04-2018
1002,      Jan-5-2018,       Jan-13-2018
1003,      Jan-14-2018,      Jan-18-2018 
1004,      Jan-25-2018,      Feb-05-2018 

所需的输出需要是:

Jan-1-2018,      Jan-18-2018 
Jan-25-2018,     Feb-05-2018 

谢谢

【问题讨论】:

  • “使用了第三方工具,只允许 sql 语句以 Select 开头” - 喜欢这些工具!不是!
  • 如何从中创建一个视图,然后在工具中使用该视图
  • 您使用的是什么版本的 SQL Server?另外,@Squirrel 的想法很棒。
  • 啊...也不能使用视图。
  • SQL Server 2012,谢谢

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


【解决方案1】:

您可以利用window functions 和称为gaps-and-islands 的概念。在您的情况下,连续日期将是岛屿,并且这些差距是不言自明的。

我以冗长的方式写下了下面的答案,以帮助弄清楚查询在做什么,但很可能以更简洁的不同方式编写。请在答案中查看我的 cmets,解释每个步骤(子查询)的作用。

--Determine Final output
select min(c.StartDate) as StartDate
, max(c.EndDate) as EndDate
from (
    --Assign a number to each group of Contiguous Records
    select b.ID
    , b.StartDate
    , b.EndDate
    , b.EndDatePrev
    , b.IslandBegin
    , sum(b.IslandBegin) over (order by b.ID asc) as IslandNbr
    from (
        --Determine if its Contiguous (IslandBegin = 1, means its not Contiguous with previous record)
        select a.ID
        , a.StartDate
        , a.EndDate
        , a.EndDatePrev
        , case when a.EndDatePrev is NULL then 1
               when datediff(d, a.EndDatePrev, a.StartDate) > 1 then 1
               else 0
          end as IslandBegin
        from (
            --Determine Prev End Date
            select tt.ID
            , tt.StartDate
            , tt.EndDate
            , lag(tt.EndDate, 1, NULL) over (order by tt.ID asc) as EndDatePrev
            from dbo.Table_Name as tt
            ) as a
        ) as b
    ) as c
group by c.IslandNbr
order by c.IslandNbr

【讨论】:

  • 非常感谢tarheel,看起来很有趣!非常感谢cmets。我今天很早就开始了,现在处于关机模式,明天早上试试,再次感谢。
【解决方案2】:

我希望以下 SQL 查询可以帮助您识别给定案例的差距和涵盖日期

我没有使用 dates table function 等的 CTE 表达式 另一方面,我使用了一个使用 master..spt_values 的数字表来生成日期表作为左连接的主表 如果不符合您的要求,您可以创建一个数字表或日期表

在查询中,为了捕捉边界之间的变化,我使用了SQL LAG() function,这使我能够与排序列表中列的先前值进行比较

select
    max(startdate) as startdate,
    max(enddate) as enddate
from (
    select 
        date,
        case when exist = 1 then date else null end as startdate,
        case when exist = 0 then dateadd(d,-1,date) else null end as enddate,
        ( row_number() over (order by date) + 1) / 2 as rn
    from (
        select date, exist, case when exist <> (lag(exist,1,'') over (order by date)) then 1 else 0 end as changed
        from (
            select 
                d.date,
                case when exists (select * from Periods where d.date between startdate and enddate) then 1 else 0 end as exist
            from (
                SELECT dateadd(dd,number,'20180101') date
                FROM master..spt_values
                 WHERE Type = 'P' and dateadd(dd,number,'20180101') <= '20180228'
            ) d 

        ) cte   
    ) tbl
    where changed = 1
) dates
group by rn

这是结果

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多