Sql server SQL Server:按超时划分的行数

Sql server SQL Server:按超时划分的行数,sql-server,tsql,sql-server-2012,olap,row-number,Sql Server,Tsql,Sql Server 2012,Olap,Row Number,我有一个包含一系列(IP varchar(15),DateTime datetime2)值的表。每行对应于用户发出的HTTP请求。我想为这些行分配会话编号。不同的IP地址有不同的会话号。如果最后一个请求超过30分钟,则应为同一IP分配新的会话号。以下是一个示例输出: IP, DateTime, SessionNumber, RequestNumber 1.1.1.1, 2012-01-01 00:01, 1, 1 1.1.1.1, 2012-01

我有一个包含一系列(IP varchar(15),DateTime datetime2)值的表。每行对应于用户发出的HTTP请求。我想为这些行分配会话编号。不同的IP地址有不同的会话号。如果最后一个请求超过30分钟,则应为同一IP分配新的会话号。以下是一个示例输出:

IP,      DateTime,         SessionNumber, RequestNumber
1.1.1.1, 2012-01-01 00:01, 1,             1
1.1.1.1, 2012-01-01 00:02, 1,             2
1.1.1.1, 2012-01-01 00:03, 1,             3
1.1.1.2, 2012-01-01 00:04, 2,             1 --different IP => new session number
1.1.1.2, 2012-01-01 00:05, 2,             2
1.1.1.2, 2012-01-01 00:40, 3,             1 --same IP, but last request 35min ago (> 30min)
第1列和第2列为输入,第3列和第4列为所需输出。该表显示了两个用户


由于基础is表确实很大,如何有效地解决这一问题?我更喜欢对数据进行少量恒定的传递(一次或两次)。

这里有几次尝试

;WITH CTE1 AS
(
SELECT *,
IIF(DATEDIFF(MINUTE,
       LAG(DateTime) OVER (PARTITION BY IP ORDER BY DateTime),
       DateTime) < 30,0,1) AS SessionFlag
FROM Sessions
), CTE2 AS
(
SELECT *,
       SUM(SessionFlag) OVER (PARTITION BY IP 
                                  ORDER BY DateTime) AS IPSessionNumber
FROM CTE1
)
SELECT IP,
       DateTime,
       DENSE_RANK() OVER (ORDER BY IP, IPSessionNumber) AS SessionNumber,
       ROW_NUMBER() OVER (PARTITION BY IP, IPSessionNumber 
                              ORDER BY DateTime) AS RequestNumber
FROM CTE2

但是,这会将排序操作的数量增加到4。

下面是一个版本,它使用表变量和行号来创建一个可以在递归CTE中使用的ID。比较游标和一个查询(由Martin提供)版本的性能可能是值得的

CREATE TABLE #T
(
  IP varchar(15),
  DateTime datetime,
  ID int,
  primary key (IP, ID)
)

insert into #T(IP, DateTime, ID)
select IP, DateTime, row_number() over(partition by IP order by DateTime) 
from #sessionRequests

;with C as
(
  select IP,
         ID,
         DateTime,
         1 as Session
  from #T
  where ID = 1
  union all 
  select T.IP,
         T.ID,
         T.DateTime,
         C.Session + case when datediff(minute, C.DateTime, T.DateTime) >= 30 then 1 else 0 end
  from #T as T
    inner join C 
      on T.IP = C.IP and
         T.ID = C.ID + 1
)
SELECT IP,
       DateTime,
       dense_rank() over(order by IP, Session) as SessionNumber,
       row_number() over(partition by IP, Session order by DateTime) as RequestNumber
from C
order by IP, DateTime, SessionNumber, RequestNumber
option (maxrecursion 0)

什么版本的SQL Server?如果2012年,新的
OVER
子句功能将有所帮助。如果来自两个IP的交叉请求,它们的会话不会混淆吗?我必须说,t-SQL缺少自定义的、有状态的聚合功能。这将使此查询变得简单,并且只需要一个排序操作。@usr-可能还值得评估一个游标和一个
#temp
表!我不确定这是否有帮助。我实际上做了一个动态光标解决方案,同时希望你能发布一个单一的查询解决方案(你做到了!)。它慢了20倍。也许我仍然会使用它,因为我可以通过更复杂的计算更好地扩展它。我们在这里所做的基本操作是“扫描”(例如)。scan=在一个有序序列上任意聚合,同时每个输入元素输出一个元素,而不是整个组/序列的一个元素。我喜欢这个版本,因为它很容易扩展,几乎像基于光标的方法。我将其更改为使用临时表,该表修复了优化器问题(表变量没有统计数据)。此外,我还验证了该代码是否有效。谢谢
CREATE TABLE #T
(
  IP varchar(15),
  DateTime datetime,
  ID int,
  primary key (IP, ID)
)

insert into #T(IP, DateTime, ID)
select IP, DateTime, row_number() over(partition by IP order by DateTime) 
from #sessionRequests

;with C as
(
  select IP,
         ID,
         DateTime,
         1 as Session
  from #T
  where ID = 1
  union all 
  select T.IP,
         T.ID,
         T.DateTime,
         C.Session + case when datediff(minute, C.DateTime, T.DateTime) >= 30 then 1 else 0 end
  from #T as T
    inner join C 
      on T.IP = C.IP and
         T.ID = C.ID + 1
)
SELECT IP,
       DateTime,
       dense_rank() over(order by IP, Session) as SessionNumber,
       row_number() over(partition by IP, Session order by DateTime) as RequestNumber
from C
order by IP, DateTime, SessionNumber, RequestNumber
option (maxrecursion 0)