使用前一行进行比较?

时间:2012-10-29 11:52:05

标签: sql sql-server sql-server-2008 select

我正在尝试找出一种比较SQL Server 2008中两行的有效方法。我需要编写一个查询,查找Movement表中连续N 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),如果我查找连续三行,则不会返回任何内容。数据的顺序仅为EventTime / DateTime。

你能给我的任何帮助都会很棒。我正在考虑使用游标,但它们通常效率很低。此外,这张表的大小约为10米,因此效率越高越好! :)

谢谢!

3 个答案:

答案 0 :(得分: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

替代最终步骤(受@ t-clausen-dk启发),以避免额外的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

答案 1 :(得分: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

答案 2 :(得分: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

修改

要回答评论,我在第一个内部查询中添加了三列。要获得第一行,您需要向WHERE子句添加pos_in_group = 1,距离就在您的手指上。

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;