2010-05-15 8 views
4

Il existe probablement une solution simple pour cela, mais je ne peux pas le voir. J'ai une table avec des dates consécutives et dupliquer souvent les données associées à plusieurs de ces dates consécutives:Comment trouver des plages de dates dans des enregistrements avec des dates consécutives et des données en double

Date  Col1 Col2 
5/13/2010 1  A 
5/14/2010 1  A 
5/15/2010 2  B 
5/16/2010 1  A 
5/17/2010 1  A 
5/18/2010 3  C 
5/19/2010 3  C 
5/20/2010 3  C 

Utilisation de MS T-SQL, je souhaite trouver les dates de début et de fin pour chaque série de valeurs distinctes col1 et col2 :

StartDate EndDate Col1 Col2 
5/13/2010 5/14/2010 1  A 
5/15/2010 5/15/2010 2  B 
5/16/2010 5/17/2010 1  A 
5/18/2010 5/20/2010 3  C 

Hypothèses: Il n'y a jamais aucune date manquante. Col1 et Col2 ne sont pas null. Des idées - de préférence n'utilisant pas de curseurs? Un grand merci, -alan

+0

si vous utilisez Sql Server 2005+, je répondrais ce http://stackoverflow.com/questions/1610599/how-can-i-do-a-contiguous-group-by-in-mysql/1611246 # 1611246 –

Répondre

1

Voici une approche utilisant outer apply. Remplacez @t par le nom de votre table.

SELECT head.date, last.date, head.col1, head.col2 
FROM  @t head 
OUTER APPLY (
      SELECT TOP 1 * 
      FROM @t t 
      WHERE t.date < head.date 
      ORDER BY t.date desc 
     ) prev 
OUTER APPLY (
      SELECT TOP 1 * 
      FROM @t t 
      WHERE t.date > head.date 
      AND (t.col1 <> head.col1 or t.col2 <> head.col2) 
      ORDER BY t.date 
     ) next 
OUTER APPLY (
      SELECT TOP 1 * 
      FROM @t t 
      WHERE (t.date < next.date or next.date is null) 
      AND (t.col1 = head.col1 and t.col2 = head.col2) 
      ORDER BY t.date 
     ) last 
WHERE (prev.col1 is null or head.col1 <> prev.col1 or head.col2 <> prev.col2) 

La requête sélectionne d'abord la ligne « de tête »: les lignes qui commencent un nouveau groupe de col1, col2. Ceci est fait en regardant la ligne "prev", et en disant qu'il doit être différent dans la clause where.

Ensuite, il recherche la fin du groupe col1, col2. C'est un processus en deux étapes: d'abord chercher la première ligne du groupe "suivant", et la ligne avant celle-ci est la "dernière" rangée.

Date  Col1 Col2 
... 
5/15/2010 2  B  <-- "prev" row 
5/16/2010 1  A  <-- "head" row 
5/17/2010 1  A  <-- "last" row 
5/18/2010 3  C  <-- "next" row 
... 

Le résultat de la requête correspond à la sortie par exemple dans votre question.

+0

Belle solution. J'aurais dû mentionner que c'est pour un ancien système SQL Server 2000. –

+0

Alan, vous devriez marquer Andomar comme la solution s'il vous convient. –

3

Pour SQL 2005+ je pense que le travail ci-dessous devrait

WITH DATES AS 
(
    SELECT COL1, COL2, DATE, 
     DATEADD(DAY, -1 * ROW_NUMBER() 
     OVER(PARTITION BY COL1, COL2 ORDER BY DATE), DATE) AS GRP 
    FROM YOUR_TABLE 
) 
SELECT COL1, COL2, MIN(DATE) AS STARTDATE, MAX(DATE) AS ENDDATE 
FROM DATES 
GROUP BY COL1, COL2, GRP 

Si vous avez des enregistrements en double, utilisez DENSE_RANK() au lieu de ROW_NUMBER()

Pour SQL 2000 il y a une sous requête et co-associés requête impliquée.

SELECT COL1, COL2, MIN(DATE) AS STARTDATE, MAX(DATE) AS ENDDATE 
FROM (SELECT COL1, COL2, DATE, 
    (SELECT MIN(DATE) 
    FROM YOUR_TABLE B 
    WHERE B.DATE >= A.DATE AND B.COL1 = A.COL1 AND B.COL2 = A.COL2 
      AND NOT EXISTS 
      (SELECT * 
      FROM YOUR_TABLE C 
      WHERE C.COL1 = B.COL1 AND C.COL2 = B.COL2 
      AND DATEDIFF(DAY, B.DATE, C.DATE) = 1) 
    ) AS GRP 
    FROM YOUR_TABLE A 
) 
GROUP BY COL1, COL2, GRP 
Questions connexes