row_number over example ejemplos sql-server tsql sql-server-2012 olap row-number

over - SQL Server: row_number particionado por timeout



sql get row_number (2)

Tengo una tabla con una serie de valores (IP varchar (15), DateTime datetime2). Cada fila corresponde a una solicitud HTTP hecha por un usuario. Quiero asignar números de sesión a estas filas. Diferentes direcciones IP tienen diferentes números de sesión. Al mismo IP se le debe asignar un nuevo número de sesión si la última solicitud es anterior a 30 minutos . Aquí hay un resultado de muestra:

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)

Las columnas 1 y 2 son entradas, 3 y 4 son las salidas deseadas. La tabla muestra dos usuarios.

Como la tabla subyacente es realmente grande, ¿cómo puede resolverse de manera eficiente ? Preferiría una pequeña cantidad constante de pases sobre los datos (uno o dos).


Aquí hay un par de intentos.

;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

Esto tiene dos operaciones de clasificación (por IP, DateTime luego por IP, IPSessionNumber ) pero supone que el SessionNumber se puede asignar arbitrariamente siempre que se asigne un número de sesión único diferente a cada nueva sesión según la dirección IP / regla de 30 minutos.

Para asignar los SessionNumber s secuencialmente en orden cronológico. Usé lo siguiente.

;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 (ORDER BY DateTime) AS GlobalSessionNo FROM CTE1 ), CTE3 AS( SELECT *, MAX(CASE WHEN SessionFlag = 1 THEN GlobalSessionNo END) OVER (PARTITION BY IP ORDER BY DateTime) AS SessionNumber FROM CTE2) SELECT IP, DateTime, SessionNumber, ROW_NUMBER() OVER (PARTITION BY SessionNumber ORDER BY DateTime) AS RequestNumber FROM CTE3

Sin embargo, esto aumenta el número de operaciones de ordenamiento a 4.


Aquí hay una versión que usa una variable de tabla y row_number para crear una ID que se puede usar en un CTE recursivo. Podría valer la pena comparar el rendimiento con el cursor y una consulta (proporcionada por 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)