web-dev-qa-db-ja.com

連続したレコードのみのROW_NUMBER()を計算できますか?

連続する値のシーケンス番号を計算する必要があります。 ROW_NUMBER()の仕事のようですね。

_DECLARE @Data TABLE
    (
    Sequence    TINYINT NOT NULL PRIMARY KEY,
    Subset  CHAR(1) NOT NULL
    )
INSERT INTO @Data (Sequence, Subset) VALUES
    (1, 'A'),
    (2, 'A'),
    (3, 'A'),
    (4, 'B'),  -- New subset
    (5, 'B'),
    (6, 'A')   -- New subset

SELECT
    Sequence, Subset,
    ROW_NUMBER() OVER (PARTITION BY Subset ORDER BY Sequence) AS SeqWithinGroup
FROM
    @Data
_

PARTITION句がSubsetの変更ごとにカウントをリセットすることを期待していましたが、代わりにSQL Serverは指定されたSubset値のすべての値を収集し、それらに番号を付けます。ここに私が期待したものと私が得たものがあります:

_Sequence Subset Expected Actual
-------- ------ -------- -----
1        A      1        1
2        A      2        2
3        A      3        3
4        B      1        1
5        B      2        2
6        A      *1*      *4*
_

SQLが行#6に達すると、サブセット「A」の番号付けを再開しますが、たまたま「A」という名前が付けられた新しいサブセットの最初の行と見なします。

デフォルトの動作ではなく、厳密にROW_NUMBER()パーティションを作成する方法はありますか?

SQLで連続した値を数えることに関して、ここや他の場所で多くの質問があります。ただし、_PARTITION BY_フィールドの繰り返し値に対処するものはまだ見ていません。ほとんどは値の増加のみを扱い、多くの場合日付を扱います。

5

私はLAG()を使用してこれに対処できました:

_SELECT
    Sequence, Subset,
    CASE WHEN Sequence = 1 OR Subset <> LAG(Subset, 1) OVER (ORDER BY Sequence)
        THEN 'New subset'
        ELSE 'Continuation'
        END
FROM
    @Data
_

これは、レコード#1、#4、および#6の「新しいサブセット」を返します。どうやらLAG()パーティションはROW_NUMBER()とは少し異なります。

明らかにこれは行番号を提供しませんが、サブセット識別子が繰り返される場合に、連続した番号のシーケンスを識別するという目標を達成するのに役立ちました。

2

私は この投稿 からの私の回答を使用して、問題に合わせて修正しました。

--Demo setup

;DECLARE @Data TABLE
    (
    Sequence    TINYINT NOT NULL PRIMARY KEY,
    Subset  CHAR(1) NOT NULL
    )
;INSERT INTO @Data (Sequence, Subset) VALUES
    (1, 'A'),
    (2, 'A'),
    (3, 'A'),
    (4, 'B'),  -- New subset
    (5, 'B'),
    (6, 'A')   -- New subset

--The solution
--Create a grouping for consecutive values called grp
;WITH _cte
AS (
    SELECT *
        ,DATEADD(DAY, - ROW_NUMBER() OVER (
                PARTITION BY Subset ORDER BY [Sequence]
                ), [Sequence]) AS grp
    FROM @Data
    )
   ,AddedRn    --add a row number for each entry in the group
AS (
    SELECT *
        ,ROW_NUMBER() OVER (
            PARTITION BY grp ORDER BY sequence
            ) AS rn
    FROM _cte
    )
--Select result ordering by Sequence
SELECT Sequence, Subset, grp, rn as SeqWithinGroup    
FROM AddedRn
order by Sequence

| Sequence | Subset | grp                     | SeqWithinGroup |
|----------|--------|-------------------------|----------------|
| 1        | A      | 1900-01-01 00:00:00.000 | 1              |
| 2        | A      | 1900-01-01 00:00:00.000 | 2              |
| 3        | A      | 1900-01-01 00:00:00.000 | 3              |
| 4        | B      | 1900-01-04 00:00:00.000 | 1              |
| 5        | B      | 1900-01-04 00:00:00.000 | 2              |
| 6        | A      | 1900-01-03 00:00:00.000 | 1              |
1
Scott Hodgin

ここで行うことは、

  1. リセットを計算する(コードの列rst
  2. sum()はグループを取得します(コードの列grp
  3. グループ化からrow_number()を取得します。

コード、

SELECT row_number() OVER (PARTITION BY grp ORDER BY sequence) AS number,
  sequence,
  subset
FROM (
  SELECT count(rst) OVER (ORDER BY sequence) AS grp, *
  FROM (
    SELECT CASE WHEN subset != lag(subset) OVER (ORDER BY sequence) THEN 1 END AS rst, *
    FROM foo
  ) AS t1
) AS t2;

DBFiddleとその結果をここで確認できます

1
Evan Carroll