2009-10-19 41 views
6

在SQL Server 2005中我有一個表的數據,看起來是這樣的:SQL分組周圍間隙

WTN------------Date 
555-111-1212 2009-01-01 
555-111-1212 2009-01-02 
555-111-1212 2009-01-03 
555-111-1212 2009-01-15 
555-111-1212 2009-01-16 
212-999-5555 2009-01-01 
212-999-5555 2009-01-10 
212-999-5555 2009-01-11 

從這個我想提取WTN,閔(日期),馬克斯(時間)的扭曲是我想也打破每當有日期的差距,所以從上面的數據,我的結果應該是這樣的:

WTN------------ MinDate---- MaxDate 
555-111-1212 2009-01-01 2009-01-03 
555-111-1212 2009-01-15 2009-01-16 
212-999-5555 2009-01-01 2009-01-01 
212-999-5555 2009-01-10 2009-01-11 
  1. 我該怎麼辦次在SQL Select/Group By中?
  2. 這可以在沒有表格或列表的情況下完成,可以枚舉我想在這些日期中確定間隔的值嗎?

回答

7

爲什麼每個人都這麼死定了反對用這種東西的桌子?數字表或日曆表佔用這麼小的空間,並且如果足夠引用足夠的話可能在內存中。你也可以使用ROW_NUMBER()方便地派生出一個數字表。使用數字表可以幫助理解查詢。但是這裏並不是一個非常簡單的例子,我從Plamen Ratchev那裏拿回來的一個竅門,希望它有幫助。

DECLARE @wtns TABLE 
(
    WTN CHAR(12), 
    [Date] SMALLDATETIME 
); 

INSERT @wtns(WTN, [Date]) 
      SELECT '555-111-1212','2009-01-01' 
UNION ALL SELECT '555-111-1212','2009-01-02' 
UNION ALL SELECT '555-111-1212','2009-01-03' 
UNION ALL SELECT '555-111-1212','2009-01-15' 
UNION ALL SELECT '555-111-1212','2009-01-16' 
UNION ALL SELECT '212-999-5555','2009-01-01' 
UNION ALL SELECT '212-999-5555','2009-01-10' 
UNION ALL SELECT '212-999-5555','2009-01-11'; 

WITH x AS 
(
    SELECT 
     [Date], 
     wtn, 
     part = DATEDIFF(DAY, 0, [Date]) 
     + DENSE_RANK() OVER 
     (
      PARTITION BY wtn 
      ORDER BY [Date] DESC 
     ) 
    FROM @wtns 
) 
SELECT 
    WTN, 
    MinDate = MIN([Date]), 
    MaxDate = MAX([Date]) 
FROM 
    x 
GROUP BY 
    part, 
    WTN 
ORDER BY 
    WTN DESC, 
    MaxDate; 
+0

爲什麼使用臨時表的東西,我可以在內嵌視圖(或CTE)呢?保存必須定義表和INSERT語句...... – 2009-10-19 18:43:25

+2

但是數字表對於許多事情非常有用,您不需要重複定義它。在我看來,這更像永久性表格。 – HLGEM 2009-10-19 18:49:38

+1

哦,不!定義一個表格?填充它?您只定義表並填充一次。現在,您可以引用該表,而不用擔心在需要序列的每個模塊中都有用於此類CTE的代碼。從理論上講,它比在運行時推導更有效率,因爲正如我之前提到的,它在大多數情況下都會記憶,並且它也應該被正確地編入索引。我在理論上說,因爲在達到某個數字/日期的某個閾值之前,您不會注意到性能差異的很大一部分。 – 2009-10-19 18:55:25

0

你的問題與INTERVAL TYPES和一個叫PACKED NORMAL FORM的關係有關。

這些問題在「時間數據和關係模型」中大量討論。

不要指望任何SQL系統真正幫助你解決這些問題。

儘管有一些教程系統,但唯一提供對這類問題的良好支持的DBMS並且我知道是我自己的。沒有鏈接,因爲我不想在這裏做太多的「堵塞」。

0

您可以用GROUP BY做到這一點,通過檢測界限:

WITH Boundaries 
     AS (
      SELECT m.WTN 
        ,m.Date 
        ,CASE WHEN p.Date IS NULL THEN 1 
         ELSE 0 
        END AS IsStart 
        ,CASE WHEN n.Date IS NULL THEN 1 
         ELSE 0 
        END AS IsEnd 
      FROM  so1590166 AS m 
      LEFT JOIN so1590166 AS p 
        ON p.WTN = m.WTN 
         AND p.Date = DATEADD(d, -1, m.Date) 
      LEFT JOIN so1590166 AS n 
        ON n.WTN = m.WTN 
         AND n.Date = DATEADD(d, 1, m.Date) 
      WHERE  p.Date IS NULL 
        OR n.Date IS NULL 
     ) 
SELECT l.WTN 
     ,l.Date AS MinDate 
     ,MIN(r.Date) AS MaxDate 
FROM Boundaries l 
INNER JOIN Boundaries r 
     ON r.WTN = l.WTN 
      AND r.Date >= l.Date 
      AND l.IsStart = 1 
      AND r.IsEnd = 1 
GROUP BY l.WTN 
     ,l.Date