2011-11-02 177 views
1

我与创建一个查询来汇总日期范围,同时利用两个场连续分组挣扎 - 基本上我试图把这个:汇总日期范围

|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| 

当然,按键,有效的简单min(DateFrom),max(DateTo)组不起作用,因为它不尊重日期范围的时间顺序。应该指出,每个密钥和有效组在日期范围内没有差距。

我已经搜索了广泛的解决方案(在这里和网络上的其他地方),并发现了很多使用OVER和CTE的组合日期的解决方案(他们都尝试过),但我认为问题在于我试图以分为两个不同的组别。我也尝试将范围转换为单独的日期,但我似乎无法按照两个组的时间顺序将它们卷起来。

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

+1

您的源数据中是否存在任何间隙或重叠? – MatBailie

+0

你可以使用任何提供的解决方案吗? –

+0

是的,现在回答 - 感谢大家的建议 - 非常感谢 – BennyD

回答

0

我没有在我面前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来说就足够了。

无论组中有多少条记录,它都能正常工作,但假设数据中没有空白或重叠。

+0

谢谢德姆工作的一种享受。 – BennyD

+0

(没有空白或重叠) – BennyD

0

您可以通过首先计算关键行(即有效或关键更改)然后链接到该组的最大日期来完成。

编辑 - 重写处理Dems标记的角落案例。这VERSON也有间隙涉及序列中

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 
+0

如果同一个(Key,Valid)组合有3个或更多的连续记录会怎么样? – MatBailie

+0

@Dems这应该仍然有效,代码通过查找没有直接前面记录的记录找到切换点。然后遍历这些“关键帧” –

+1

对不起,我的坏。我只剔除了查询并假定它的作用 - 错误。有一个角落的情况,这可能无法处理,但...如果我添加记录'| 1 | 0 | 2001-03-16 | 2001-03-31 |',最终结果是否仍显示'DateTo'值为'2001-03-15'? – MatBailie

0

我不能拿出任何短使用光标。但是,这确实有效:

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 
+0

正如答案的开头所暗示的那样;虽然这确实有效,但游标经常会产生沉重的成本 - 如果有一套基于集合的方法,通常值得首先探究... – MatBailie