2

我想知道如何在 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 表,但我不知道如何只为一个连续的日期块返回一行。

4

2 回答 2

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 小提琴

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

于 2013-09-24T04:58:58.443 回答
0

您可以使用递归公用表表达式来做到这一点:

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毫秒
  • 带 CTE 的山羊 CO 查询:10804毫秒
  • 带固定数字表的山羊 CO 查询:7毫秒

5行的结果,大句号

sql fiddle demo

  • 我的查询:1毫秒
  • 带 CTE 的山羊 CO 查询:700毫秒
  • 带固定数字表的山羊 CO 查询:36毫秒
于 2013-09-24T05:18:40.170 回答