web-dev-qa-db-ja.com

範囲内の行のグループ化

次の(不自然な)監視データのテーブルがあるとします。

RowID       DateStamp            Success         Error
-----       -----------------    -------         -----------
1001        5/24/2019 11:23am    1               None 
1004        5/24/2019 11:24am    1               None 
1005        5/24/2019 11:25am    1               None 
1009        5/24/2019 11:26am    0               SQL Timeout
1018        5/24/2019 11:27am    0               SQL Timeout 
1019        5/24/2019 11:28am    1               None 
1026        5/24/2019 11:29am    1               None 
1035        5/24/2019 11:30am    0               Planned Maintenance
1100        5/24/2019 11:31am    0               Planned Maintenance
1111        5/24/2019 11:32am    1               None 

同じステータスとエラーの行をグループ化したいのですが、行が順番に並んでいる場合のみ日付またはIDで(IDは正しく順序付けされますが、保証されていません)順次)、次のように:

Starting             Ending               Polls  Success   Error
-----------------    -----------------    -----  -------   -----------
5/24/2019 11:23am    5/24/2019 11:25am    3      1         None
5/24/2019 11:26am    5/24/2019 11:27am    2      0         SQL Timeout
5/24/2019 11:28am    5/24/2019 11:29am    2      1         None
5/24/2019 11:30am    5/24/2019 11:31am    2      0         Planned Maintenance
5/24/2019 11:32am    5/24/2019 11:32am    1      1         None

シンプルな GROUP BYが機能しません:

SELECT MIN(DateStamp) as Starting, MAX(DateStamp) as Ending, 
      Count(*) as Polls, Success, Error
FROM myTable
GROUP BY Success, Error
ORDER BY Starting

いつ発生したかに関係なく、すべての行が同じステータス/エラーに結合されるため、次のようになります。

Starting             Ending               Polls  Success   Error
-----------------    -----------------    -----  -------   -----------
5/24/2019 11:23am    5/24/2019 11:32am    6      1         None
5/24/2019 11:26am    5/24/2019 11:27am    2      0         SQL Timeout
5/24/2019 11:30am    5/24/2019 11:31am    2      0         Planned Maintenance

SQL Server 2012でこれを行う簡単な方法はありますか?

4
BradC

アイランドを特定する別の方法は、LAG分析関数とSUM()ウィンドウ集約関数を使用することです。

WITH
  sentinels AS
  (
    SELECT
      DateStamp,
      Success,
      Error,
      Grp = CASE
              WHEN Success = LAG(Success) OVER (ORDER BY DateStamp ASC)
               AND Error   = LAG(Error  ) OVER (ORDER BY DateStamp ASC)
              THEN 0
              ELSE 1
            END
    FROM
      dbo.myTable
  ),
  fullyMarked AS
  (
    SELECT
      DateStamp,
      Success,
      Error,
      Grp = SUM(Grp) OVER (ORDER BY DateStamp ASC ROWS UNBOUNDED PRECEDING)
    FROM
      sentinels
  )
SELECT
  Starting  = MIN(DateStamp),
  Ending    = MAX(DateStamp),
  Polls     = COUNT(*),
  Success,
  Error
FROM
  fullyMarked
GROUP BY
  Success,
  Error,
  Grp
ORDER BY
  Starting ASC
;

sentinels CTEは、各アイランドの最初の行を1でマークし、他の行を0でマークします。

RowID  DateStamp          Success  Error                Grp
-----  -----------------  -------  -------------------  ---
1001   5/24/2019 11:23am  1        None                 1
1004   5/24/2019 11:24am  1        None                 0
1005   5/24/2019 11:25am  1        None                 0
1009   5/24/2019 11:26am  0        SQL Timeout          1
1018   5/24/2019 11:27am  0        SQL Timeout          0
1019   5/24/2019 11:28am  1        None                 1
1026   5/24/2019 11:29am  1        None                 0
1035   5/24/2019 11:30am  0        Planned Maintenance  1
1100   5/24/2019 11:31am  0        Planned Maintenance  0
1111   5/24/2019 11:32am  1        None                 1

fullyMarked CTEはsentinelsの出力を受け取り、Grp列の現在までの合計を計算します。これにより、各アイランドに一意のIDを効果的に割り当てます。

RowID  DateStamp          Success  Error                Grp
-----  -----------------  -------  -------------------  ---
1001   5/24/2019 11:23am  1        None                 1    -- 1
1004   5/24/2019 11:24am  1        None                 1    -- 1+0
1005   5/24/2019 11:25am  1        None                 1    -- 1+0+0
1009   5/24/2019 11:26am  0        SQL Timeout          2    -- 1+0+0+1
1018   5/24/2019 11:27am  0        SQL Timeout          2    -- 1+0+0+1+0
1019   5/24/2019 11:28am  1        None                 3    -- 1+0+0+1+0+1
1026   5/24/2019 11:29am  1        None                 3    -- 1+0+0+1+0+1+0
1035   5/24/2019 11:30am  0        Planned Maintenance  4    -- 1+0+0+1+0+1+0+1
1100   5/24/2019 11:31am  0        Planned Maintenance  4    -- 1+0+0+1+0+1+0+1+0
1111   5/24/2019 11:32am  1        None                 5    -- 1+0+0+1+0+1+0+1+0+1

最後のステップは、Success, Error, Grpおよび必要に応じて他の列のデータを集計します。これにより、期待どおりの結果が得られます。

あなたはこれを使うことができます ライブデモ dbfiddle logoこのソリューションを試すためのdb <> fiddle.uk。

3
Andriy M

これは、ROW_NUMBER()を使用してグループを作成する1つの例です。

_;WITH CTE_GROUP AS (
  SELECT 
  Success, 
  Error,
  DateStamp,
  ROW_NUMBER() OVER (PARTITION BY Success, Error ORDER BY DateStamp)
      - ROW_NUMBER() OVER (ORDER BY DateStamp) as SuccesErrorDateStamp,
FROM dbo.myTable
)
SELECT MIN(DateStamp) as Starting, MAX(DateStamp) as Ending, 
      Count(*) as Polls, Success, Error
FROM CTE_GROUP
GROUP BY Success, Error,SuccesErrorDateStamp
ORDER BY Starting;
_

結果

_Starting                    Ending                      Polls   Success Error
2019-05-24 11:23:00.0000000 2019-05-24 11:25:00.0000000 3       1       None
2019-05-24 11:26:00.0000000 2019-05-24 11:27:00.0000000 2       0       SQL Timeout
2019-05-24 11:28:00.0000000 2019-05-24 11:29:00.0000000 2       1       None
2019-05-24 11:30:00.0000000 2019-05-24 11:31:00.0000000 2       0       Planned Maintenance
2019-05-24 11:32:00.0000000 2019-05-24 11:32:00.0000000 1       1       None
_

DB <>フィドル

ありがとう @ AndriyM 必要なのはROW_NUMBER()列が1つだけであることを指摘してください

2
Randi Vertongen