根据日期组合SQL记录

时间:2015-01-16 03:00:06

标签: sql sql-server

我有下表:

startDate   endDate
----------------------
01-01-2014  01-07-2014
01-08-2014  01-14-2014
01-15-2014  01-21-2014

01-28-2014  02-03-2014
02-04-2014  02-10-2014

我想将日期捆绑在一起以最小化记录数量。输出结果应如下所示:

startDate   endDate
----------------------
01-01-2014  01-21-2014
01-28-2014  02-10-2014

如果一条记录的startDate比另一条记录的endDate晚一天,则日期可以链接在一起。

这可以在不使用游标的情况下实现吗?

5 个答案:

答案 0 :(得分:3)

正如评论中所述,您需要Recursive CTEWindow Function来汇总连续的天数

;WITH cte
     AS (SELECT StartDate,
                EndDate
         FROM   yourtable
         UNION ALL
         SELECT a.StartDate,
                b.EndDate
         FROM   cte a
                JOIN yourtable b
                  ON Dateadd(DAY, 1, a.EndDate) = b.StartDate),
     cte1
     AS (SELECT StartDate,
                EndDate,
                Row_number()
                  OVER(
                    partition BY EndDate
                    ORDER BY StartDate ASC) AS rn
         FROM   cte)
SELECT StartDate,
       Max(EndDate) AS EndDate
FROM   cte1 a
WHERE  a.rn = 1
GROUP  BY StartDate
ORDER  BY EndDate 

SQLFIDDLE DEMO

答案 1 :(得分:3)

注意:以下代码与SQL Server 2012兼容

使用以下代码:

SELECT MIN(startdate), MAX(enddate) 
FROM
(
    SELECT *, SUM(diff) OVER(ORDER BY startdate) AS cat FROM
    (
        SELECT 
            startdate, 
            endDate, 
            CASE WHEN DATEDIFF("dd", LAG(enddate) OVER (ORDER BY startdate), startdate) > 1 THEN 1 ELSE 0 END AS diff 
        FROM <Your Table>
    ) t
) t
GROUP BY cat

发生了什么事?

上面的代码使用窗口函数来检测上一个结束日期并检查它与当前开始日期之间的差异,如果存在差异则放置1,否则放置0。结果表应如下所示:

startdate   enddate     diff
2014-01-01  2014-01-07  0
2014-01-08  2014-01-14  0
2014-01-15  2014-01-21  0
2014-01-28  2014-02-03  1
2014-02-04  2014-02-10  0
2014-02-11  2014-03-04  0
2014-03-14  2014-03-21  1
2014-04-01  2014-05-10  1

使用SUM函数作为窗函数从当前行到第一行求和,结果为:

startdate   endDate   diff  cat
2014-01-01  2014-01-07  0   0
2014-01-08  2014-01-14  0   0
2014-01-15  2014-01-21  0   0
2014-01-28  2014-02-03  1   1
2014-02-04  2014-02-10  0   1
2014-02-11  2014-03-04  0   1
2014-03-14  2014-03-21  1   2
2014-04-01  2014-05-10  1   3

然后,您可以通过为每个MIN(startdate)获取MAX(enddate)cat来轻松汇总这些内容。

答案 2 :(得分:1)

您可以在没有递归CTE的情况下执行此操作。您只需要识别开始每个序列的记录,然后使用聚合。

with cte as (
      select t.*, (case when tprev.startdate is null then 1 else 0 end) as IsSeqStart
      from table t left join
           table tprev
           on t.startdate = dateadd(day, 1, tprev.enddate)
     )
select min(startdate) as startdate, max(enddate) as enddate
from (select cte.*, sum(isSeqStart) over (order by startdate) as grp
      from cte
     ) t
group by grp

答案 3 :(得分:1)

此解决方案假设连续日期范围可能重叠

declare @t table (startDate date, endDate date)

insert into @t
values 
    ('01-01-2014', '01-07-2014'),
    ('01-08-2014', '01-14-2014'),
    ('01-15-2014', '01-21-2014'),
    ('01-28-2014', '02-03-2014'),
    ('02-04-2014', '02-10-2014')

;with cte 
as (select startdate, enddate from @t
    union all
    select cte.startdate, t.enddate
    from cte 
        inner join @t t
            on t.startdate between dateadd(dd,1,cte.startDate) and dateadd(dd,1,cte.enddate)
            and t.endDate > cte.endDate
    )
select min(startdate) startdate, enddate
from
    (select startDate, max(enddate) enddate 
    from cte 
    group by startdate) a
group by enddate

答案 4 :(得分:1)

您也可以尝试以下方法

 ;WITH cte AS
 (
  SELECT ROW_NUMBER() OVER(ORDER BY t1.startDate) AS Id, t1.StartDate
  FROM dbo.test104 t1 LEFT JOIN dbo.test104 t2 ON DATEADD(DAY, -1, t1.startDate) = t2.endDate
  WHERE t2.endDate IS NULL
  ), cte2 AS
  ( 
   SELECT ROW_NUMBER() OVER(ORDER BY t1.EndDate) AS Id, t1.EndDate
   FROM dbo.test104 t1 LEFT JOIN dbo.test104 t2 ON DATEADD(DAY, 1, t1.EndDate) = t2.StartDate
   WHERE t2.startDate IS NULL
   )
   SELECT *
   FROM cte c1 JOIN cte2 c2 ON c1.Id = c2.Id

查看SQLFiddle