Row_Numberを使用して連続した行数を見つける


8

信号の発生を表すintのこの列があり、連続する行の数を示す列を追加しようとしています

私のデータがこのように見える場合

724
727
728
733
735
737
743
747
749

連続する行数列を持つ結果のデータは次のようになります

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

ループ関数を使用してそれを実行しましたが、cteを使用して理解しようとしています。これが私の最新の試みのサンプルです

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

これらの結果を生成します

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

最初の明らかな問題は、シリーズの最初の信号がないことです。それを除けば、これを連続マーカーのrow_numberパーティションで別のcteに渡すことができると思いました。それはそれを1つのパーティションとしてパーティション化したため、それは機能しませんでした。あるシリーズが次のシリーズと分離していることをパーティション分割方法に示す方法が見つかりませんでした

どんな助けでもありがたいです。


1
ソースデータと目的の結果の間に不一致があるようです。
マーティン・スミス

回答:


16

このタイプのクエリの一般的な名前は「ギャップとアイランド」です。以下の1つのアプローチ。ソースデータに重複がある可能性がある場合dense_rankは、row_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

戻り値

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

1

SQL 2012では、LAGとウィンドウ関数を使用してこれを行うこともできます。

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

-1

このような問題の場合はいつものように、Java、C ++、またはC#で簡単に実行できます。

本当にデータベースで行う必要がある場合は、Oracleなどの高速カーソルでRDBMSを使用し、単純なカーソルを記述して、複雑なものを記述することなく高速パフォーマンスを楽しむことができます。

T-SQLで実行する必要があり、データベース設計を変更できない場合、Itzik Ben-Ganは「MVP Deep Dives vol 1」でいくつかのソリューションを記述し、ウィンドウ関数に関する彼の新しい本でOLAP関数を使用したいくつかの新しいソリューションを記述しています。 SQL 2012で。

別の方法として、別の列連続マーカーをテーブルに追加して、事前計算された値をそのテーブルに格納することもできます。制約を使用して、事前に計算されたデータが常に有効であることを確認できます。興味のある方はご説明します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.