2010-12-01 7 views
13

Dire que j'ai le tableau suivant:groupe sql par des lignes seulement qui sont dans l'ordre

MyTable 
--------- 
| 1 | A | 
| 2 | A | 
| 3 | A | 
| 4 | B | 
| 5 | B | 
| 6 | B | 
| 7 | A | 
| 8 | A | 
--------- 

J'ai besoin de la requête SQL pour génèrerait les éléments suivants:

--------- 
| 3 | A | 
| 3 | B | 
| 2 | A | 
--------- 

En fait, je fais un group by mais seulement pour les rangées qui sont ensemble dans la séquence. Des idées?

Notez que la base de données est sur sql server 2008. Il y a un post sur ce sujet mais il utilise la fonction lag() d'oracle.

Répondre

22

Ceci est connu comme le problème des "îles". En utilisant l'approche de Itzik Ben Gan:

;WITH YourTable AS 
(
SELECT 1 AS N, 'A' AS C UNION ALL 
SELECT 2 AS N, 'A' AS C UNION ALL 
SELECT 3 AS N, 'A' AS C UNION ALL 
SELECT 4 AS N, 'B' AS C UNION ALL 
SELECT 5 AS N, 'B' AS C UNION ALL 
SELECT 6 AS N, 'B' AS C UNION ALL 
SELECT 7 AS N, 'A' AS C UNION ALL 
SELECT 8 AS N, 'A' AS C 
), 
    T 
    AS (SELECT N, 
       C, 
       DENSE_RANK() OVER (ORDER BY N) - 
       DENSE_RANK() OVER (PARTITION BY C ORDER BY N) AS Grp 
     FROM YourTable) 
SELECT COUNT(*), 
     C 
FROM T 
GROUP BY C, 
      Grp 
ORDER BY MIN(N) 
+1

Une solution fantastique! Cela va dans la boîte à outils. –

+0

Un lien qui pourrait être utile http://stackoverflow.com/q/14755122/247184 – VoodooChild

0

cela fonctionnera pour vous ...

SELECT 
    Total=COUNT(*), C 
FROM 
(
SELECT 
NGroup = ROW_NUMBER() OVER (ORDER BY N) - ROW_NUMBER() OVER (PARTITION BY C ORDER BY N), 
N, 
C 
FROM MyTable 
)RegroupedTable 
GROUP BY C,NGroup 
0

Juste pour le plaisir, sans fonctions spécifiques SQL et pas en supposant que la colonne ID augmente de façon monotone:

WITH starters(name, minid, maxid) AS (
    SELECT 
     a.name, MIN(a.id), MAX(a.id) 
    FROM 
     mytable a RIGHT JOIN 
     mytable b ON 
      (a.name <> b.name AND a.id < b.id) 
    WHERE 
     a.id IS NOT NULL 
    GROUP BY 
     a.name 
), 
both(name, minid, maxid) AS (
    SELECT 
     name, minid, maxid 
    FROM 
     starters 
    UNION ALL 
    SELECT 
     name, MIN(id), MAX(id) 
    FROM 
     mytable 
    WHERE 
     id > (SELECT MAX(maxid) from starters) 
    GROUP BY 
     name 
) 
SELECT 
    COUNT(*), m.name, minid 
FROM 
    both INNER JOIN 
    mytable m ON 
     id BETWEEN minid AND maxid 
GROUP BY 
    m.name, minid 

Résultat (ignorer la colonne midid):

(No column name) name minid 
3 A 1 
3 B 4 
2 A 7