Sql 聚合日期范围

Sql 聚合日期范围,sql,sql-server-2008,tsql,Sql,Sql Server 2008,Tsql,我正在努力创建一个查询来汇总一个日期范围,同时按两个字段连续分组-基本上,我正在尝试改变这一点: |Key|Valid|DateFrom |DateTo | | 1| 0|2001-01-01|2001-01-31| | 1| 0|2001-02-01|2001-02-20| | 1| 1|2001-02-21|2001-02-28| | 1| 0|2001-03-01|2001-03-15| | 2| 1|2001-01-01|2001-01-3

我正在努力创建一个查询来汇总一个日期范围,同时按两个字段连续分组-基本上,我正在尝试改变这一点:

|Key|Valid|DateFrom  |DateTo    |
|  1|    0|2001-01-01|2001-01-31|
|  1|    0|2001-02-01|2001-02-20|
|  1|    1|2001-02-21|2001-02-28|
|  1|    0|2001-03-01|2001-03-15|
|  2|    1|2001-01-01|2001-01-31|
|  2|    0|2001-02-01|2001-02-20|
|  2|    0|2001-02-21|2001-02-28|
|  2|    1|2001-03-01|2001-03-15|
为此:

|Key|Valid|DateFrom  |DateTo    |
|  1|    0|2001-01-01|2001-02-20|
|  1|    1|2001-02-21|2001-02-28|
|  1|    0|2001-03-01|2001-03-15|
|  2|    1|2001-01-01|2001-01-31|
|  2|    0|2001-02-01|2001-02-28|
|  2|    1|2001-03-01|2001-03-15|
当然,一个简单的minDateFrom,MaxDateToGroup by Key,Valid不起作用,因为它不尊重日期范围的时间顺序。应该注意的是,在每个键和有效组内的日期范围中没有间隔

我在这里和网上的其他地方搜索了很多解决方案,找到了很多使用OVER和CTE对日期进行分组的解决方案,但我认为问题是我正在尝试将两个不同的分组考虑在内。我也尝试过将范围转换为单独的日期,但我似乎无法按时间顺序将它们按两组进行汇总


任何帮助都将不胜感激。谢谢。

您可以先计算关键行,即有效或关键更改的位置,然后链接到该组的最大日期

编辑-重写以处理DEM标记的角案例。这节经文还涉及序列中的间隙

with keyItems as ( 
  -- First find all the "Key Frames" 
  select d.* 
from  
  data d 
left outer join data d2  
  on d.[Key]=d2.[key] and d.valid=d2.valid and d.dateFrom = DateAdd(d,1,d2.dateto) 
where d2.[key] is null 
), 
ordered as ( 
  -- This is to provide a sequence number for the main query against these key frames 
  select  
    ROW_NUMBER() over (partition by [key] order by datefrom) as row, 
    * 
  from keyItems 
),
rangeends([key],row,dateto) as (
select o.[key],o.row-1,MAX(d.DateTo)
from ordered o left outer join data d on d.[key]=o.[key] and d.DateTo < o.DateFrom
group by o.[key],o.row-1
union all
select o.[key],MAX(o.row),MAX(d.dateto)
from ordered o inner join data d on d.[key]=o.[key] 
group by o.[key]
)
select 
    o1.[Key], 
    o1.Valid, 
    o1.DateFrom, 
    coalesce(r.dateto,o1.dateTo) as DateTo 
    from ordered o1 
    left outer join rangeends r on r.[key]=o1.[Key] and r.row=o1.row

我面前没有SQL客户端,但您可以这样做

WITH
  sequenced_data
AS
(
  SELECT
    ROW_NUMBER() OVER (PARTITION BY Key        ORDER BY DateFrom) AS KeyRow,
    ROW_NUMBER() OVER (PARTITION BY Key, Valid ORDER BY DateFrom) AS KeyValidRow,
    *
  FROM
    yourData
)
SELECT
  Key,
  Valid,
  MIN(DateFrom) AS DateFrom,
  MAX(DatTo)    AS DateTo
FROM
  sequenced_data
GROUP BY
  Key,
  Valid,
  KeyRow - KeyValidRow
ORDER BY
  Key,
  MIN(DateFrom)
用你的数据进行可视化

|Key|Valid|DateFrom  |DateTo    |KeyRow|KeyValidRow|KeyRow - KeyValidRow
|  1|    0|2001-01-01|2001-01-31|     1|          1|       0
|  1|    0|2001-02-01|2001-02-20|     2|          2|       0
|  1|    1|2001-02-21|2001-02-28|     3|          1|       2
|  1|    0|2001-03-01|2001-03-15|     4|          3|       1
|  2|    1|2001-01-01|2001-01-31|     1|          1|       0
|  2|    0|2001-02-01|2001-02-20|     2|          1|       1
|  2|    0|2001-02-21|2001-02-28|     3|          2|       1
|  2|    1|2001-03-01|2001-03-15|     4|          2|       2
虽然KeyRow-KeyValidRow不一定告诉您很多,但它确实为每个组提供了一个不同的值,因此对于group BY来说就足够了


无论一个组中有多少条记录,它都能工作,但它确实假设数据中没有间隙或重叠。

除了使用光标,我想不出任何东西。但是,这确实有效:

declare @example table (tKey int, Valid int, DateFrom date, DateTo date);

insert into @example values (1, 0, '2001-01-01', '2001-01-31');
insert into @example values (1, 0, '2001-02-01', '2001-02-20');
insert into @example values (1, 1, '2001-02-21', '2001-02-28');
insert into @example values (1, 0, '2001-03-01', '2001-03-15');
insert into @example values (2, 1, '2001-01-01', '2001-01-31');
insert into @example values (2, 0, '2001-02-01', '2001-02-20');
insert into @example values (2, 0, '2001-02-21', '2001-02-28');
insert into @example values (2, 1, '2001-03-01', '2001-03-15');

declare @output table (tKey int, Valid int, DateFrom date, DateTo date);

DECLARE ex_cursor CURSOR FOR
    select 
        tKey,Valid,DateFrom,DateTo
    from 
        @example
    order by tKey, DateFrom

DECLARE @tKey int
DECLARE @Valid int
DECLARE @DateFrom date
DECLARE @DateTo date

DECLARE @last_tKey int
DECLARE @last_Valid int
DECLARE @min_Date date
DECLARE @max_Date date

OPEN ex_cursor;

FETCH NEXT FROM ex_cursor
INTO @tKey, @Valid, @DateFrom, @DateTo;
SET @last_tKey = @tKey;
SET @last_Valid = @Valid;
SET @min_Date = @DateFrom;
SET @max_Date = @DateTo;

WHILE @@FETCH_STATUS = 0
BEGIN
    IF (@last_tKey <> @tKey OR @last_Valid <> @Valid)
        BEGIN
            -- output results
            INSERT INTO @output SELECT @last_tKey, @last_Valid, @min_Date, @max_Date
            -- reset values
            SET @last_tKey = @tKey;
            SET @last_Valid = @Valid;
            SET @min_Date = @DateFrom;
            SET @max_Date = @DateTo;
        END
    ELSE
        BEGIN
            IF (@DateTo > @max_Date) SET @max_Date = @DateTo
        END
    FETCH NEXT FROM ex_cursor
    INTO @tKey, @Valid, @DateFrom, @DateTo
END 
-- output one more time at end
INSERT INTO @output SELECT @last_tKey, @last_Valid, @min_Date, @max_Date
CLOSE ex_cursor;
DEALLOCATE ex_cursor;

SELECT * FROM @output ORDER BY tKey, DateFrom

您的源数据中是否存在任何缺口或重叠?您可以使用任何提供的解决方案吗?是的,现在回答-感谢大家的建议-非常感激如果同一个密钥、有效组合有3个或更多连续记录会怎么样?@Dems这应该仍然有效,代码通过查找没有直接前面记录的记录来查找切换点。然后在那些关键的框架上迭代,我的坏。我只是粗略地浏览了一下这个问题,并假设它的作用是错误的。有一个角落的情况,这可能无法处理,虽然。。。如果我添加了记录| 1 | 0 | 2001-03-16 | 2001-03-31 |,最终结果是否仍然显示日期到值2001-03-15?我现在已经进行了适当的更正,代码现在将处理序列中的角情况和间隙。重叠是完全不同的情况!正如答案开头所暗示的那样;虽然这确实有效,但游标通常会产生巨大的成本——如果有一种基于集合的方法,那么首先需要探讨的是……没有间隙或重叠