2009-10-19 40 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