3

SQL Server 2008で2つの行を比較する効率的な方法を見つけようとしています。Movementテーブル内で、Speed < 10連続してN回あるすべての行を検索するクエリを作成する必要があります。

テーブルの構造は次のとおりです。

EventTimeスピード

データが次の場合:

2012-02-05 13:56:36.980, 2
2012-02-05 13:57:36.980, 11
2012-02-05 13:57:46.980, 2
2012-02-05 13:59:36.980, 2
2012-02-05 14:06:36.980, 22
2012-02-05 15:56:36.980, 2

次に、2つの連続する行を検索した場合は行3/4(13:57:46.980 / 13:59:36.980)を返し、3つの連続した行を検索した場合は何も返しません。データの順序はEventTime/DateTimeのみです。

あなたが私に与えることができるどんな助けも素晴らしいでしょう。カーソルの使用を検討していますが、通常はかなり非効率的です。また、このテーブルのサイズは約10m行なので、効率が高いほど優れています。:)

ありがとう!

4

3 に答える 3

5
DECLARE
  @n             INT,
  @speed_limit   INT
SELECT
  @n             = 5,
  @speed_limit   = 10

;WITH
  partitioned AS
(
  SELECT
    *,
    CASE WHEN speed < @speed_limit THEN 1 ELSE 0 END   AS PartitionID
  FROM
    Movement
)
,
  sequenced AS
(
  SELECT
    ROW_NUMBER() OVER (                         ORDER BY EventTime) AS MasterSeqID,
    ROW_NUMBER() OVER (PARTITION BY PartitionID ORDER BY EventTime) AS PartIDSeqID,
    *
  FROM
    partitioned
)
,
  filter AS
(
  SELECT
    MasterSeqID - PartIDSeqID    AS GroupID,
    MIN(MasterSeqID)             AS GroupFirstMastSeqID,
    MAX(MasterSeqID)             AS GroupFinalMastSeqID
  FROM
    sequenced
  WHERE
    PartitionID = 1
  GROUP BY
    MasterSeqID - PartIDSeqID
  HAVING
    COUNT(*) >= @n
)
SELECT
  sequenced.*
FROM
  filter
INNER JOIN
  sequenced
    ON  sequenced.MasterSeqID >= filter.GroupFirstMastSeqID
    AND sequenced.MasterSeqID <= filter.GroupFinalMastSeqID

追加の. _ JOIN両方をテストして、どちらがよりパフォーマンスが高いかを確認します。

,
  filter AS
(
  SELECT
    MasterSeqID - PartIDSeqID                              AS GroupID,
    COUNT(*) OVER (PARTITION BY MasterSeqID - PartIDSeqID) AS GroupSize,
    *
  FROM
    sequenced
  WHERE
    PartitionID = 1
)
SELECT
  *
FROM
  filter
WHERE
  GroupSize >= @n
于 2012-10-29T12:14:56.437 に答える
3
declare @t table(EventTime datetime, Speed int)
insert @t values('2012-02-05 13:56:36.980', 2)
insert @t values('2012-02-05 13:57:36.980', 11)
insert @t values('2012-02-05 13:57:46.980', 2)
insert @t values('2012-02-05 13:59:36.980', 2)
insert @t values('2012-02-05 14:06:36.980', 22)
insert @t values('2012-02-05 15:56:36.980', 2)

declare @N int = 1

;with a as
(
  select EventTime, Speed, row_number() over (order by EventTime) rn from @t
), b as
(
  select EventTime, Speed, 1 grp,  rn from a where rn = 1
  union all
  select a.EventTime, a.Speed, case when a.speed < 10 and b.speed < 10 then grp else grp + 1 end, a.rn
  from a join b on a.rn = b.rn+1
), c as
(
  select EventTime, Speed, count(*) over (partition by grp) cnt from b
)
select * from c
where cnt > @N
OPTION (MAXRECURSION 0) -- Thx Dems
于 2012-10-29T12:32:03.840 に答える
3

Dems とほぼ同じアイデアですが、少し異なります。

select * from (
 select eventtime, speed, rnk, new_rnk, 
      rnk - new_rnk,
      max(rnk) over (partition by speed, new_rnk-rnk) -
      min(rnk) over (partition by speed, new_rnk-rnk) + 1  as no_consec
  from (
     select eventtime, rnk, speed,
            row_number() over (partition by speed order by eventtime) as new_rnk
     from (
             select eventtime, speed,
             row_number() over (order by eventtime) as rnk
             from a 
          ) a
     where a.speed < 5
  )
order by eventtime
  )
where no_consec >= 2;

5 は速度制限、2 は連続イベントの最小数です。create database の記述を簡単にするために、日付を数字として入れました。

SQLFIDDLE

編集:

コメントに答えるために、最初の内部クエリに 3 つの列を追加しました。pos_in_group = 1最初の行のみを取得するには、 to WHERE 句を追加する必要があり、距離は指先にあります。

SQLFIDDLE

select eventtime, speed, min_date, max_date, pos_in_group

from (
  select eventtime, speed, rnk, new_rnk, 
      rnk - new_rnk,
      row_number() over (partition by speed, new_rnk-rnk order by eventtime) pos_in_group,
      min(eventtime) over (partition by speed, new_rnk-rnk) min_date,
      max(eventtime) over (partition by speed, new_rnk-rnk) max_date,
      max(rnk) over (partition by speed, new_rnk-rnk) -
      min(rnk) over (partition by speed, new_rnk-rnk) + 1  as no_consec
  from (
     select eventtime, rnk, speed,
            row_number() over (partition by speed order by eventtime) as new_rnk
     from (
             select eventtime, speed,
             row_number() over (order by eventtime) as rnk
             from a 
          ) a
     where a.speed < 5
     )
  order by eventtime
  )
where no_consec > 1;
于 2012-10-29T12:45:18.243 に答える