Usando Row_Number para encontrar a contagem de linhas consecutivas

8

Eu tenho essa coluna de entradas que representam a ocorrência de um sinal e estou tentando adicionar uma coluna que mostra a contagem de linhas consecutivas

Se meus dados forem assim

724
727
728
733
735
737
743
747
749

os dados resultantes com uma coluna consecutiva de contagem de linhas ficariam assim

724 1
727 1
728 2
729 3
735 1
737 1
743 1
744 2
748 1

Eu fiz isso usando uma função de loop, mas estou tentando descobrir usando um cte. Aqui está uma amostra da minha última tentativa

DECLARE @d TABLE ( signal INT )
INSERT  INTO @d
        SELECT  724
        UNION
        SELECT  727
        UNION
        SELECT  728
        UNION
        SELECT  729
        UNION
        SELECT  735
        UNION
        SELECT  737
        UNION
        SELECT  743
        UNION
        SELECT  744
        UNION
        SELECT  748 ;
WITH    a AS ( SELECT   signal,
                        ROW_NUMBER() OVER ( ORDER BY signal ) AS marker
               FROM     @d
             ) ,
        b AS ( SELECT   a1.signal,
                        CASE ( a1.signal - a2.signal )
                          WHEN 1 THEN 1
                          ELSE 0
                        END consecutiveMarker
               FROM     a a1
                        INNER JOIN a a2 ON a2.marker = a1.marker - 1
             )
    SELECT  *
    FROM    b

Produz esses resultados

signal  consecutiveMarker
727 0
728 1
729 1
735 0
737 0
743 0
744 1
748 0

A primeira questão óbvia está faltando o primeiro sinal de uma série. Exceto isso, pensei que poderia passar isso para outro cte com um particionamento row_number no consecutivoMarker. Isso não funcionou porque o particionou como uma partição. Não consegui encontrar uma maneira de indicar ao método de particionamento que uma série é separada da próxima

Qualquer ajuda é apreciada.

OrangeYoda
fonte
11
Parece haver uma incompatibilidade entre os dados de origem e os resultados desejados.
Martin Smith

Respostas:

16

O nome geral para esse tipo de consulta é "lacunas e ilhas". Uma abordagem abaixo. Se você puder ter duplicatas nos dados de origem, poderá precisar, em dense_rankvez derow_number

WITH DATA(C) AS
(
SELECT 724 UNION ALL
SELECT 727 UNION ALL
SELECT 728 UNION ALL
SELECT 729 UNION ALL
SELECT 735 UNION ALL
SELECT 737 UNION ALL
SELECT 743 UNION ALL
SELECT 744 UNION ALL
SELECT 747 UNION ALL
SELECT 749
), T1 AS
(
SELECT C,
       C - ROW_NUMBER() OVER (ORDER BY C) AS Grp
FROM DATA)
SELECT C,
       ROW_NUMBER() OVER (PARTITION BY Grp ORDER BY C) AS Consecutive
FROM T1

Devoluções

C           Consecutive
----------- --------------------
724         1
727         1
728         2
729         3
735         1
737         1
743         1
744         2
747         1
749         1
Martin Smith
fonte
1

No SQL 2012, você também pode fazer isso usando o LAG e as funções da janela, por exemplo

DECLARE @d TABLE ( signal INT PRIMARY KEY) 

INSERT INTO @d 
VALUES
    ( 724 ),
    ( 727 ),
    ( 728 ),
    ( 729 ),
    ( 735 ),
    ( 737 ),
    ( 743 ),
    ( 744 ),
    ( 748 )

SELECT signal
    , 1 + ( SUM( is_group ) OVER ( ORDER BY signal ROWS BETWEEN 1 PRECEDING AND 0 FOLLOWING ) * is_group )
FROM
    (
    SELECT *
        , CASE WHEN LAG(signal) OVER( ORDER BY signal ) = signal - 1 THEN 1 ELSE 0 END is_group
    FROM @d
    ) x
wBob
fonte
-1

Como de costume com esses problemas, é muito fácil realizar em Java ou C ++ ou C #.

Se você realmente precisar fazer isso no banco de dados, poderá usar um RDBMS com cursores rápidos, como Oracle, escrever um cursor simples e desfrutar de um desempenho rápido sem precisar escrever nada complexo.

Se você precisa fazer isso no T-SQL e não pode alterar o design do banco de dados, Itzik Ben-Gan escreveu várias soluções no "MVP Deep Dives vol 1" e algumas novas soluções usando as funções OLAP em seu novo livro sobre funções de janela no SQL 2012.

Como alternativa, você pode adicionar outra coluna consecutiveMarker à sua tabela e armazenar valores pré-calculados nela. Podemos usar restrições para garantir que os dados pré-calculados sejam sempre válidos. Se alguém estiver interessado, eu posso explicar como.

AK
fonte