Utilisation de Row_Number pour trouver le nombre de lignes consécutives

8

J'ai cette colonne d'entiers qui représente une occurrence d'un signal et j'essaie d'ajouter une colonne qui montre le nombre de lignes consécutives

Si mes données ressemblent à ceci

724
727
728
733
735
737
743
747
749

les données résultantes avec une colonne de nombre de lignes consécutives ressembleraient à ceci

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

Je l'ai fait en utilisant une fonction de boucle mais j'essaie de comprendre en utilisant un cte. Voici un échantillon de ma dernière tentative

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

Produit ces résultats

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

Le premier problème évident manque le premier signal d'une série. Sauf cela, je pensais que je pourrais ensuite passer cela à un autre cte avec un partitionnement row_number sur le Marker consécutif. Cela n'a pas fonctionné car il l'a partitionné en une seule partition. Je n'ai pas trouvé de moyen d'indiquer à la méthode de partitionnement qu'une série est distincte de la suivante

Toute aide est appréciée.

OrangeYoda
la source
1
Il semble y avoir un décalage entre les données source et les résultats souhaités.
Martin Smith

Réponses:

16

Le nom général de ce type de requête est "lacunes et îlots". Une approche ci-dessous. Si vous pouvez avoir des doublons dans les données source dont vous pourriez avoir besoin dense_rankplutôt querow_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

Retour

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

Dans SQL 2012, vous pouvez également le faire en utilisant LAG et les fonctions de fenêtre, par exemple

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
la source
-1

Comme d'habitude avec de tels problèmes, il est très facile à réaliser en Java ou C ++ ou C #.

Si vous avez vraiment besoin de le faire dans la base de données, vous pouvez utiliser un SGBDR avec des curseurs rapides, comme Oracle, écrire un simple curseur et profiter de performances rapides sans avoir à écrire quoi que ce soit de complexe.

Si vous devez le faire en T-SQL et que vous ne pouvez pas changer la conception de la base de données, Itzik Ben-Gan a écrit plusieurs solutions dans "MVP Deep Dives vol 1", et quelques nouvelles solutions utilisant les fonctions OLAP dans son nouveau livre sur les fonctions de fenêtre dans SQL 2012.

Alternativement, vous pouvez ajouter une autre colonne consécutive Marker à votre table et y stocker des valeurs précalculées. Nous pouvons utiliser des contraintes pour garantir que les données pré-calculées sont toujours valides. Si quelqu'un est intéressé, je peux vous expliquer comment.

AK
la source