合并相邻期间的差异小于x(例如x = 3)天的日期

时间:2016-02-29 12:14:01

标签: sql-server tsql

如果相邻期间之间的差异小于x(例如x = 3)天,如何合并日期期间。在下面的示例中,期间3的开始日期和期间2的结束日期之间的差异为2.So第三期和第二期合并为单期,如下所示。但期间4的开始日期与期间3的结束日期之间的差异为3.因此,它们不会合并并作为单独的期间处理。

startdate                  enddate
-------------------------------------------------------
2004-12-29 00:00:00.000 2004-12-29 00:00:00.000
2005-11-25 00:00:00.000 2005-11-25 00:00:00.000
2005-11-27 00:00:00.000 2005-12-09 00:00:00.000
2005-12-12 00:00:00.000 2005-12-17 00:00:00.000
2005-12-19 00:00:00.000 2005-12-20 00:00:00.000

输出:

 startdate                  enddate
---------------------------------------------------------

2004-12-29 00:00:00.000 2004-12-29 00:00:00.000
2005-11-25 00:00:00.000 2005-12-09 00:00:00.000
2005-12-12 00:00:00.000 2005-12-20 00:00:00.000

2 个答案:

答案 0 :(得分:1)

set dateformat dmy;
declare @t table (id int identity, startdate smalldatetime, enddate smalldatetime);
insert into @t (startdate, enddate) values ('02.01.2015', '03.01.2015');
insert into @t (startdate, enddate) values ('06.01.2015', '06.01.2015');
insert into @t (startdate, enddate) values ('10.01.2015', '11.01.2015');
declare @x int = 3;
declare @toDel table(id int);

update
  a
set
  a.enddate = b.enddate
output
  b.id
-- save merged id, than should be deleted
into
  @toDel (id)
from
  @t a
  join @t b on datediff(day, a.enddate, b.startdate) between 0 and @x
               and a.id != b.id
;


-- delete merged id
delete from @t where id in (select d.id from @toDel d);

select * from @t;

答案 1 :(得分:1)

首先需要识别相邻的记录。对于这些记录,您只需要保留一个具有组合日期范围的记录,其他记录可以简单地传递:

 ;WITH  adjacent
        AS ( SELECT a.startdate AS startDateA ,
                    a.enddate AS endDateA ,
                    b.startdate AS startDateB ,
                    b.enddate AS endDateB
            FROM   dbo.data a
                   LEFT JOIN dbo.data b ON DATEDIFF(day, a.enddate,
                                             b.startdate) <= 3
                                      AND a.startdate < b.startdate
          )
   SELECT a.startDateA AS StartDate ,
          ISNULL(a.endDateB, a.endDateA) AS EndDate
   FROM  adjacent a
   LEFT JOIN adjacent b ON  a.startDateA = b.startDateB
                             AND a.endDateA = b.endDateB                                
   WHERE b.startDateA IS NULL 
   ORDER BY a.startDateA

这是我用过的测试脚本:

CREATE TABLE dbo.data(startdate DATETIME, enddate DATETIME)


INSERT INTO dbo.data
      SELECT '2 Jan 2015' ,
             '3 Jan 2015'
      UNION
      SELECT '6 Jan 2015' ,
             '6 Jan 2015'
      UNION
      SELECT  '10 Jan 2015' ,
              '11 Jan 2015'

编辑:如果您需要在函数中合并多个日期范围(例如x = 10),则上述解决方案将无效,您必须使用下面的递归cte。根据你的评论,我还在一个函数中重构了这段代码:

CREATE TYPE daterangetype AS TABLE
(
startdate DATETIME NOT NULL,
enddate DATETIME NOT NULL
)
GO
CREATE FUNCTION dbo.fn_Merge_DateRanges
    (
     @data daterangetype READONLY ,
     @dayrange INT 
    )
RETURNS @merged TABLE
    (
     startdate DATETIME ,
     endate DATETIME
    )
AS 
    BEGIN
       WITH  cte0
               AS ( SELECT    ROW_NUMBER() OVER ( ORDER BY startdate ) AS RowId ,
                          startdate ,
                          enddate
                   FROM   @data
                 ),
             cte1
               AS ( SELECT    t.RowId ,
                          t.startdate ,
                          t.enddate ,
                          t2.startdate AS Nextstartdate
                   FROM   cte0 AS t
                          LEFT OUTER JOIN cte0 AS t2 ON t2.RowId > t.RowId
                                                  AND DATEDIFF(day,
                                                  t.enddate,
                                                  t2.startdate) < @dayrange
                 ),
             cte2
               AS ( SELECT    c.RowId ,
                          c.startdate ,
                          c.enddate ,
                          c.Nextstartdate
                   FROM   cte1 AS c
                   WHERE      c.Nextstartdate IS NULL
                   UNION ALL
                   SELECT     c2.RowId ,
                          c.startdate ,
                          c2.enddate ,
                          c2.Nextstartdate
                   FROM   cte2 AS c2
                          INNER JOIN cte1 AS c ON c.Nextstartdate = c2.startdate
                 )
          INSERT    INTO @merged
                SELECT  MIN(startdate) AS startdate ,
                       enddate
                FROM       cte2
                GROUP BY RowId ,
                       enddate
                ORDER BY RowId ,
                       startdate

       RETURN

    END

然后你可以这样调用你的函数:

DECLARE @data daterangetype

SET DATEFORMAT YMD

INSERT  INTO @data
       SELECT    '2004-12-29 00:00:00.000' ,
             '2004-12-29 00:00:00.000'
       UNION ALL
       SELECT    '2005-11-25 00:00:00.000' ,
             '2005-11-25 00:00:00.000'
       UNION ALL
       SELECT    '2005-11-27 00:00:00.000' ,
             '2005-12-09 00:00:00.000'
       UNION ALL
       SELECT    '2005-12-12 00:00:00.000' ,
             '2005-12-17 00:00:00.000'
       UNION ALL
       SELECT    '2005-12-19 00:00:00.000' ,
             '2005-12-20 00:00:00.000'    


SELECT  * FROM     dbo.fn_Merge_DateRanges(@data, 5)