Únete Tiempo clasificado en el rango de una sola vez para obtener la brecha:
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
continuación, puede utilizar muchos métodos para identificar el máximo de separación, cuando empezó etc.
actualización
Mi respuesta original fue escrita desde una base de datos Mac w/oa para probar. Tuve más tiempo para jugar con este problema y probar y medir cómo funciona en una tabla de registros de 1M. Mi tabla de prueba se define así:
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
Para seleccionar el registro para cualquier información, mi respuesta preferida hasta el momento es la siguiente:
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;
De registro 1M, ~ 47k usuarios distintos, el resultado de esto se devuelve en 1 ms en mi instancia de prueba insignificante (memoria caché caliente), lecturas de 48 páginas.
Si se elimina el UserId = 42, la brecha máxima y el tiempo que pasó para cada usuario (con duplicados para múltiples espacios máximos) necesitan 6379139 lecturas, bastante pesadas y 14 en mi máquina de prueba.
El tiempo puede reducirse a la mitad si sólo el ID de usuario y brecha máximo es necesaria (sin información cuando se produjo la brecha max):
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
Esto sólo necesita 3.193.448 lee, sólo la mitad en comparación con el anterior y completado en 6 segundos en registros de 1M. La diferencia se debe a que la versión anterior necesitaba evaluar cada brecha una vez para encontrar la máxima, luego evaluarlas nuevamente para encontrar las que son iguales al máximo. Tenga en cuenta que, para estos resultados de rendimiento, la estructura de la tabla que propuse con un índice en (UserId, Time) es critical.
En cuanto al uso de CTE y 'particiones' (mejor conocidas como funciones de clasificación): todo esto es ANSI SQL-99 y es compatible con la mayoría de los proveedores. El único constructo específico de SQL Server fue el uso de la función datediff
, que ahora se elimina. Tengo la sensación de que algunos lectores entienden 'agnóstico' como el 'mínimo común denominador SQL entendido también por mi proveedor favorito'. También tenga en cuenta que el uso de expresiones de tabla comunes y operador de aplicación cruzada se utilizan únicamente para mejorar la legibilidad de la consulta. Ambos pueden reemplazarse con una tabla derivada usando un reemplazo simple y mecánico. Aquí está la consulta muy igual donde los CTE se reemplazaron con tablas derivadas. Voy a dejar que juzgan en su legibilidad en comparación con el CTE basa uno:
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
Maldición, era esperada terminará lol más enrevesado. Esto es bastante legible porque solo tenía dos CTE para empezar. Aún así, en consultas con 5-6 tablas derivadas, el formulario CTE es mucho más legible.
Para completar, aquí es la misma transformación aplicada a mi consulta simplificada (sólo lagunas max, sin hora de finalización y el acceso brecha 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
Puede aclarar: ¿está buscando la brecha más grande entre los registros * adyacentes * cuando se clasifica por ID y se filtra por usuario, o la brecha más grande entre * dos registros * para el mismo usuario? Para cualquiera, la respuesta es 2 para su caso de prueba. – richardtallent
@richardtellent: Estoy buscando el espacio más largo entre las entradas de usuario "adyacentes", donde "adyacente" significa que no hay ninguna entrada de fecha y hora entre ellas (y no está basada en ID). Espero que eso se aclare. No estoy seguro de haber entendido su segunda explicación, porque la brecha más grande entre dos registros se encuentra entre el primero (1) y el último (4). –