SQL группа только по строкам, которые находятся в последовательности - PullRequest
13 голосов
/ 01 декабря 2010

Скажите, у меня есть следующая таблица:

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

Мне нужен запрос sql для вывода следующего:

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

В основном я делаю group by, но только для строк, которые вместе в последовательности. Есть идеи?

Обратите внимание, что база данных находится на сервере SQL Server 2008. В этой теме есть сообщение, однако в нем используется функция oracle lag ().

Ответы [ 3 ]

23 голосов
/ 01 декабря 2010

Это известно как проблема "островов". Используя подход Ицик Бен Ган:

;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)
0 голосов
/ 01 декабря 2010

Просто для удовольствия, без каких-либо специфических для SQL функций и НЕ предполагая, что столбец ID монотонно увеличивается:

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

Результат (игнорируйте средний столбец):

(No column name)    name    minid
3   A   1
3   B   4
2   A   7
0 голосов
/ 01 декабря 2010

это будет работать для вас ...

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
...