2009-10-19 9 views
6

En SQL Server 2005 tengo una tabla con los datos que se ve algo como esto:SQL Agrupación alrededor de lagunas

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 

A partir de este me gustaría extraer WTN, Min (Fecha), Max (Fecha) del giro es me gustaría también romper cada vez que hay un vacío en las fechas, por lo que a partir de los datos anteriores, mis resultados deben verse como:

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. ¿Cómo puedo hacer º está en un SQL Select/Group By?
  2. ¿Se puede hacer esto sin una tabla o lista que enumere los valores en los que quiero identificar las lagunas (Fechas aquí)?

Respuesta

7

¿Por qué todos están tan en contra de usar una mesa para este tipo de cosas? Una tabla de números o una tabla de calendario ocupa tan poco espacio y probablemente esté en la memoria si se hace referencia a ellos de todos modos. También puede derivar una tabla de números con bastante facilidad sobre la marcha usando ROW_NUMBER(). Usar una tabla de números puede ayudar a comprender la consulta. Pero aquí hay un ejemplo no tan sencillo, un truco que recogí de Plamen Ratchev hace un tiempo, espero que ayude.

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

¿Por qué usar una tabla temporal para algo que pueda hacer en una vista en línea (o CTE)? Guarda tener que definir la tabla y las instrucciones INSERT ... –

+2

Pero una tabla de números es tan útil para tantas cosas que no debería necesitar definirla repetidamente. Esto es mejor como mesa permanente en mi opinión. – HLGEM

+1

¡Oh, no! Definiendo una mesa? ¿Poblarlo? Usted solo define la tabla y la rellena una vez. Ahora puede hacer referencia a esa tabla y no preocuparse por tener un código para dicho CTE en cada módulo donde necesite una secuencia. En teoría, será más eficiente que derivar en tiempo de ejecución porque, como mencioné anteriormente, estará en la memoria en la mayoría de los casos, y también debería estar correctamente indexado. Digo en teoría porque no notará gran parte de una diferencia de rendimiento hasta que llegue a un determinado umbral de números/fechas. –

0

Su problema tiene que ver con los tipos de intervalo y una cosa llamada PACKED forma normal de una relación.

Los problemas se discuten en general en "Datos temporales y el modelo relacional".

No espere que ningún sistema SQL realmente lo ayude con tales problemas.

A pesar de algunos sistemas tutoriales, el único DBMS que ofrece soporte decente para tales problemas, y que yo sepa, es mío. No hay enlace porque no quiero hacer demasiado "conectar" aquí.

0

Puede hacer esto con la GROUP BY, mediante la detección de los límites:

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