GROUP BY для непрерывных строк в SQL - PullRequest
4 голосов
/ 16 июля 2009

С учетом следующей таблицы:

<b>ID   State  Date</b>
12   1      2009-07-16 10:00
45   <b>2</b>      2009-07-16 13:00
67   <b>2</b>      2009-07-16 14:40
77   1      2009-07-16 15:00
89   1      2009-07-16 15:30
99   1      2009-07-16 16:00

Вопрос:
Как я могу группировать по полю «Состояние», сохраняя границы между изменениями состояния?

SELECT MIN(ID) AS ID, State, MIN(Date) AS Date, COUNT(ID) AS Count
FROM table GROUP BY State

приводит к следующему:

<b>ID   State  Date              Count</b>
12   1      2009-07-16 10:00  4
45   2      2009-07-16 13:00  2


но это ожидается:

<b>ID   State  Date              Count</b>
12   1      2009-07-16 10:00  1
45   2      2009-07-16 13:00  2
77   1      2009-07-16 15:00  3


Возможно ли это в SQL? Пока я не нашел решения ...

Ответы [ 4 ]

10 голосов
/ 16 июля 2009
SELECT  MIN(id) AS id, MIN(ts) AS ts, MIN(state) AS state, COUNT(*) cnt
FROM    (
        SELECT  @r := @r + (@state != state) AS gn,
                @state := state AS sn,
                s.*
        FROM    (
                SELECT  @r := 0,
                        @state := 0
                ) vars,
                t_state s
        ORDER BY
                ts
        ) q
GROUP BY
        gn

Скрипты создания таблиц для тестирования:

CREATE TABLE t_state (id INT NOT NULL PRIMARY KEY, state INT NOT NULL, ts DATETIME NOT NULL);

INSERT
INTO  t_state
VALUES
(12,   1,      '2009-07-16 10:00'),
(45,   2,      '2009-07-16 13:00'),
(67,   2,      '2009-07-16 14:40'),
(77,   1,      '2009-07-16 15:00'),
(89,   1,      '2009-07-16 15:30'),
(99,   1,      '2009-07-16 16:00');
2 голосов
/ 16 июля 2009

Вот как это сделать с CTE на сервере MSSQL

-- DROP TABLE MyLog
CREATE TABLE MyLog(
        ID          INT PRIMARY KEY
        , State     INT
        , Date      DATETIME
        )
INSERT MyLog
SELECT 12, 1, '2009-07-16 10:00' UNION ALL
SELECT 45, 2, '2009-07-16 13:00' UNION ALL
SELECT 67, 2, '2009-07-16 14:40' UNION ALL
SELECT 77, 1, '2009-07-16 15:00' UNION ALL
SELECT 89, 1, '2009-07-16 15:30' UNION ALL
SELECT 99, 1, '2009-07-16 16:00'

;WITH   CTE
AS      (
        SELECT  ROW_NUMBER() OVER(ORDER BY ID) AS RowNo
                , *
        FROM    MyLog
        )
, MyLogGroup
AS      (
        SELECT  l.*
                , ( SELECT  MAX(ID)
                    FROM    CTE c
                    WHERE   NOT EXISTS (SELECT * FROM CTE
                                        WHERE RowNo = c.RowNo-1 AND State = c.State)
                            AND c.ID <= l.ID) AS GroupID
        FROM    MyLog l
        )
SELECT  *
FROM    MyLogGroup
1 голос
/ 19 января 2012

Здесь - более подробное описание того, как работают решения, подобные предложенному Quassnoi

0 голосов
/ 16 июля 2009

Я мог бы заявить об очевидном здесь, но если вы хотите использовать Transact-SQL, вы можете перебирать строки таблицы и создавать свой собственный набор результатов, что, вероятно, кажется хлопотным, но это определенно будет работать. Итерация может быть сделана без использования курсоров .

...