2011-11-02 137 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