加入對一次性級中的時間,以獲得差距:
with cte_ranked as (
select *, row_number() over (partition by UserId order by Time) as rn
from table)
select l.*, datediff(minute, r.Time, l.Time) as gap_length
from cte_ranked l join cte_ranked r on l.UserId = r.UserId and l.rn = r.rn-1
然後,您可以用很多方法來識別的最大間隙,當它開始等
更新
我原來的答案是從Mac w/oa數據庫寫入測試。我有更多時間來處理這個問題,並且實際測試並測量它在1M記錄表上的表現。我的測試表的定義是這樣的:
create table access (id int identity(1,1)
, UserId int not null
, Time datetime not null);
create clustered index cdx_access on access(UserID, Time);
go
對於選擇記錄的任何信息,我的首選答案,到目前爲止是這樣的:
with cte_gap as (
select Id, UserId, a.Time, (a.Time - prev.Time) as gap
from access a
cross apply (
select top(1) Time
from access b
where a.UserId = b.UserId
and a.Time > b.Time
order by Time desc) as prev)
, cte_max_gap as (
select UserId, max(gap) as max_gap
from cte_gap
group by UserId)
select g.*
from cte_gap g
join cte_max_gap m on m.UserId = g.UserId and m.max_gap = g.gap
where g.UserId = 42;
從1M記錄,〜47K不同用戶的結果:這是在我的測試puny實例(熱緩存)1ms內返回,48頁讀取。
如果刪除了UserId = 42過濾器,每個用戶的最大間隔和時間(包含多個最大間隔的重複數據)需要6379139個讀取,而且很重,並且在我的測試機器上需要14s。
的時間,如果只有用戶名和最大的差距是需要(無信息當發生的最大間隙)被切成兩半:
select UserId, max(a.Time-prev.Time) as gap
from access a
cross apply (
select top(1) Time
from access b
where a.UserId = b.UserId
and a.Time > b.Time
order by Time desc
) as prev
group by UserId
這隻需要3193448讀取,只有一半相比前,並在1M記錄中以6秒完成。之所以會出現這種差異,是因爲以前的版本需要一次評估每個間隙以找到最大值,然後再次評估它們以找出與最大值相等的值。請注意,對於此性能結果,我建議的索引(UserId,Time)的表的結構爲至關重要。至於CTE和'分區'(更好的稱爲排名函數)的使用:這是所有ANSI SQL-99,並且得到大多數供應商的支持。唯一的SQL Server特定結構是使用datediff
函數,該函數現在被刪除。我感覺有些讀者將'不可知論'理解爲'我最喜歡的供應商也理解的最不常見的分母SQL'。另請注意,使用公用表表達式和交叉應用運算符僅用於提高查詢的可讀性。兩者都可以使用簡單的機械替換方法替換派生表。這裏是非常相同查詢哪裏的CTE用派生表代替。我要讓你評價自己的可讀性與基於CTE的相比:
select g.*
from (
select Id, UserId, a.Time, (a.Time - (
select top(1) Time
from access b
where a.UserId = b.UserId
and a.Time > b.Time
order by Time desc
)) as gap
from access a) as g
join (
select UserId, max(gap) as max_gap
from (
select Id, UserId, a.Time, (a.Time - (
select top(1) Time
from access b
where a.UserId = b.UserId
and a.Time > b.Time
order by Time desc
)) as gap
from access a) as cte_gap
group by UserId) as m on m.UserId = g.UserId and m.max_gap = g.gap
where g.UserId = 42
媽的,我跳最終會更令人費解的笑。這是非常可讀的,因爲它只有兩個CTE開始。儘管如此,在使用5-6派生表的查詢中,CTE表單的方式更具可讀性。
爲了完整起見,這裏是適用於我的一個簡化查詢(僅限最大的差距,沒有差距結束時間和訪問ID)相同的變換:
select UserId, max(gap)
from (
select UserId, a.Time-(
select top(1) Time
from access b
where a.UserId = b.UserId
and a.Time > b.Time
order by Time desc) as gap
from access a) as gaps
group by UserId
您能否澄清一下:您是否在按ID排序並按用戶過濾時,或者是同一用戶的任意兩個*記錄之間的最大差距,查找*相鄰*記錄之間的最大差距?對於任何一個,你的測試用例的答案是2。 – richardtallent 2009-08-23 15:07:11
@richardtellent:我正在尋找「相鄰」用戶條目之間最長的間隔,其中「相鄰」表示它們之間沒有日期時間條目(而不是基於ID)。我希望澄清。我不確定我是否理解你的第二個解釋,因爲任何兩個記錄之間的最大差距在第一個(1)和最後一個(4)之間。 – 2009-08-23 15:21:17