我正在尝试找出一种比较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米,因此效率越高越好! :)
谢谢!
答案 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,我将日期作为数字。
修改强>
要回答评论,我在第一个内部查询中添加了三列。要获得第一行,您需要向WHERE子句添加pos_in_group = 1
,距离就在您的手指上。
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;