6

私の問題は単純です。一連のステータスとタイムスタンプ(好奇心のために、これらのステータスはアラームレベルを示します)を含むテーブルがあり、2つのステータス間の期間を取得するためにこのテーブルをクエリしたいと思います。

単純に見えますが、ここで注意が必要な部分があります。ルックアップテーブル、手順を作成できません。このテーブルは10億を超えるレコードを保持する小さなモンスターであるため、できるだけ高速にする必要があります(冗談ではありません!)...

スキーマは非常に単純です。

[pk]時間値

(実際には、2番目のpkがありますが、これには役に立ちません)

そして実際の例の下に:

タイムスタンプステータス
2013-1-1 00:00:00 1
2013-1-1 00:00:05 2
2013-1-1 00:00:10 2
2013-1-1 00:00:15 2
2013-1-1 00:00:20 0
2013-1-1 00:00:25 1
2013-1-1 00:00:30 2
2013-1-1 00:00:35 2
2013-1-1 00:00:40 0

レベル2アラームのみを考慮した場合、出力は次のようになります。レベル2アラームの開始と終了(0に達したとき)を報告する必要があります。

StartTime EndTime Interval
2013-1-1 00:00:05 2013-1-1 00:00:20 15
2013-1-1 00:00:30 2013-1-1 00:00:40 10

私はあらゆる種類の内部結合を試みてきましたが、それらすべてが私を驚くべきデカルト爆発に導きます。皆さんは私がこれを達成する方法を見つけるのを手伝ってくれますか?

ありがとう!

4

4 に答える 4

4

これは私が今日見た難しい質問の1つでなければなりません-ありがとう!CTEを使用できると思いますか?もしそうなら、このようなことを試してください:

;WITH Filtered
AS
(
    SELECT ROW_NUMBER() OVER (ORDER BY dateField) RN, dateField, Status
    FROM Test    
)
SELECT F1.RN, F3.MinRN,
    F1.dateField StartDate,
    F2.dateField Enddate
FROM Filtered      F1, Filtered F2, (
SELECT F1a.RN, MIN(F3a.RN) as MinRN
FROM Filtered      F1a
   JOIN Filtered F2a ON F1a.RN = F2a.RN+1 AND F1a.Status = 2 AND F2a.Status <> 2
   JOIN Filtered F3a ON F1a.RN < F3a.RN AND F3a.Status <> 2
GROUP BY F1a.RN ) F3 
WHERE F1.RN = F3.RN AND F2.RN = F3.MinRN

そしてフィドル。間隔は追加しませんでしたが、ここからその部分を処理できると思います。

幸運を。

于 2013-01-24T20:51:14.527 に答える
0

代わりをするためだけに。パフォーマンスのテストを試みましたが、終了しませんでした。

SELECT
  MIN([main].[Start]) AS [Start],
  [main].[End],
  DATEDIFF(s, MIN([main].[Start]), [main].[End]) AS [Seconds]
FROM
(
  SELECT
    [sub].[Start],
    MIN([sub].[End]) AS [End]
  FROM
  (
    SELECT
      [start].[Timestamp] AS [Start],
      [start].[Status] AS [StartingStatus],
      [end].[Timestamp] AS [End],
      [end].[Status] AS [EndingStatus]
    FROM [Alerts] [start],  [Alerts] [end]
    WHERE [start].[Status] = 2 
      AND [start].[Timestamp] < [end].[Timestamp]
      AND [start].[Status] <> [end].[Status]
  ) AS [sub]
  GROUP BY
    [sub].[Start],
    [sub].[StartingStatus]
) AS [main]
GROUP BY
  [main].[End]

そして、ここにフィドルがあります。

于 2013-01-24T22:29:42.043 に答える
0

最後に、私が満足しているバージョンを見つけました。2つの(増加する)シーケンスの違いは常に一定であることが指摘された別の質問(ただし、どれを思い出せません)からの答えを思い出すのに時間がかかりました。

WITH Ordered (occurredAt, status, row, grp) 
             as (SELECT occurredAt, status, 
                        ROW_NUMBER() OVER (ORDER BY occurredat), 
                        ROW_NUMBER() OVER (PARTITION BY status 
                                           ORDER BY occurredat)
                 FROM Alert)

SELECT Event.startDate, Ending.occurredAt as endDate,
       DATEDIFF(second, Event.startDate, Ending.occurredAt) as interval

FROM (SELECT MIN(occurredAt) as startDate, MAX(row) as ending
      FROM Ordered
      WHERE status = 2
      GROUP BY row - grp) Event

LEFT JOIN (SELECT occurredAt, row
           FROM Ordered
           WHERE status != 2) Ending
        ON Event.ending + 1 = Ending.row

(作業チェック用の追加のデータ行を含む、 作業中のSQL Fiddle の例)。

残念ながら、これは最終行であるレベル 2 ステータス (動作が未規定) を正しく処理しませんが、リスト表示されます。

于 2013-01-24T21:42:49.837 に答える
-1

テーブルのアイデンティティである id を使用して、同様のことを行います。

    create table test(id int primary key identity(1,1),timstamp datetime,val int)

    insert into test(timstamp,val) Values('1/1/2013 00:00:00',1)
    insert into test(timstamp,val) Values('1/1/2013 00:00:05',2)
    insert into test(timstamp,val) Values('1/1/2013 00:00:25',1)
    insert into test(timstamp,val) Values('1/1/2013 00:00:30',2)
    insert into test(timstamp,val) Values('1/1/2013 00:00:35',1)

    select t1.timstamp,t1.val,DATEDIFF(s,t1.timstamp,t2.timstamp) 
    from test t1 left join test t2 on t1.id=t2.id-1

    drop table test

また、タイムスタンプを 1980 年または 2000 年以降の秒数にすることもできます。ただし、常に逆変換を実行したくない場合は、実際のタイム スタンプを使用する頻度によって異なります。

于 2013-01-24T21:34:06.023 に答える