SQL中相邻日期的连接

时间:2013-09-24 02:40:02

标签: sql sql-server common-table-expression

我想知道如何在sql中进行相邻日期范围的交叉或连接。

我有一个客户开始和结束日期列表,例如(以dd / mm / yyyy格式,其中31/12/9999表示客户仍然是当前客户)。

CustID | StartDate |  Enddate |
1      | 01/08/2011|19/06/2012|
1      | 20/06/2012|07/03/2012|
1      | 03/05/2012|31/12/9999|
2      | 09/03/2009|16/08/2009|
2      | 16/01/2010|10/10/2010|
2      | 11/10/2010|31/12/9999|
3      | 01/08/2010|19/08/2010|
3      | 20/08/2010|26/12/2011|

虽然不同行中的日期不重叠,但我会将某些范围视为一个有争议的时间段,例如,当开始日期是结束日期后一天(对于给定的客户)。因此,我想返回一个只返回日期交集的查询

CustID | StartDate |  Enddate |
1      | 01/08/2011|07/03/2012|
1      | 03/05/2012|31/12/9999|
2      | 09/03/2009|16/08/2009|
2      | 16/01/2010|31/12/9999|
3      | 01/08/2010|26/12/2011|

我查看了CTE表,但我无法弄清楚如何为一个连续的日期块返回一行。

2 个答案:

答案 0 :(得分:1)

这应该在2005年推进:

;WITH cte2 AS (SELECT 0 AS Number
               UNION ALL
               SELECT Number + 1
               FROM cte2
               WHERE Number < 10000)
SELECT CustID, Min(GroupStart) StartDate, MAX(EndDate) EndDate
FROM (SELECT *
           , DATEADD(DAY,b.number,a.StartDate) GroupStart
           , DATEADD(DAY,1- DENSE_RANK() OVER (PARTITION BY CustID ORDER BY DATEADD(DAY,b.number,a.StartDate)),DATEADD(DAY,b.number,a.StartDate)) GroupDate
    FROM Table1 a
    JOIN  cte2 b
      ON b.number <= DATEDIFF(d, startdate, EndDate)
) X
GROUP BY CustID, GroupDate
ORDER BY CustID, StartDate
OPTION (MAXRECURSION 0)

演示:SQL Fiddle

您可以构建一个数字0的快速表,其大小足以覆盖范围中日期的范围以替换cte,因此它不会每次运行,正确索引它将快速运行。

答案 1 :(得分:0)

您可以使用recursive common table expression执行此操作:

with cte as (
    select t.CustID, t.StartDate, t.EndDate, t2.StartDate as NextStartDate
    from Table1 as t
        left outer join Table1 as t2 on t2.CustID = t.CustID and t2.StartDate = case when t.EndDate < '99991231' then dateadd(dd, 1, t.EndDate) end
), cte2 as (
    select c.CustID, c.StartDate, c.EndDate, c.NextStartDate
    from cte as c
    where c.NextStartDate is null
    union all
    select c.CustID, c.StartDate, c2.EndDate, c2.NextStartDate
    from cte2 as c2
        inner join cte as c on c.CustID = c2.CustID and c.NextStartDate = c2.StartDate
)
select CustID, min(StartDate) as StartDate, EndDate
from cte2
group by CustID, EndDate
order by CustID, StartDate
option (maxrecursion 0);

<强> sql fiddle demo

快速性能测试:

750 行的结果,小周期的2天长度:

<强> sql fiddle demo

  • 我的查询: 300 ms
  • 使用CTE进行山羊CO查询: 10804 ms
  • Goat CO查询与固定数字表: 7 ms

5 行的结果,大周期

<强> sql fiddle demo

  • 我的查询: 1 ms
  • 使用CTE进行Goat CO查询: 700 ms
  • Goat CO查询固定数字表: 36 ms