Histograma basado en fechas no estándar de SQL Server
-
06-07-2019 - |
Pregunta
Tengo datos de inicio de sesión de usuario con marcas de tiempo y lo que me gustaría hacer es obtener un histograma de inicios de sesión por año, pero el año comienza en una fecha arbitraria. Por ejemplo, quiero el siguiente tipo de información:
1 May 2005 - 30 Apr 2006 | 525
1 May 2006 - 30 Apr 2007 | 673
1 May 2007 - 30 Apr 2008 | 892
1 May 2006 - 30 Apr 2009 | 1047
Las etiquetas en la primera columna no son importantes, pero los intervalos de fechas sí lo son. Sé que puedo dividirlo por años estrechos con:
SELECT YEAR([date]) AS [year], COUNT(*) AS cnt
FROM logins
GROUP BY YEAR([date])
ORDER BY [year]
Pero eso no me da los rangos de datos que quiero. ¿Cómo se puede hacer esto?
Solución
declare @baseDate datetime
set @baseDate = '1 May 2005'
SELECT
datediff(year, @baseDate, [date]) AS YearBucket
,COUNT(*) AS cnt
FROM logins
GROUP BY datediff(year, @baseDate, [date])
ORDER BY datediff(year, @baseDate, [date])
EDITAR - disculpas, estás en lo correcto. Aquí hay una versión fija (debería haber usado una tabla de prueba para comenzar ...)
create table logins (date datetime, foo int)
insert logins values ('1 may 2005', 1)
insert logins values ('1 apr 2006', 2)
insert logins values ('1 may 2006', 3)
declare @baseDate datetime
set @baseDate = '1 May 2005'
SELECT
datediff(day, @baseDate, [date]) / 365 AS YearBucket
,COUNT(*) AS cnt
FROM logins
GROUP BY datediff(day, @baseDate, [date]) / 365
ORDER BY datediff(day, @baseDate, [date]) / 365
Cambie las unidades con fecha si desea más granularidad que días.
EDIT # 2 - ok, aquí hay una solución más robusta que maneja los años bisiestos :) EDITAR # 3: en realidad, esto no maneja los años bisiestos, sino que permite especificar intervalos de tiempo variables. Vaya con dateadd (año, 1, @baseDate) para el enfoque seguro del año bisiesto.
declare @baseDate datetime, @interval datetime
--@interval is expressed as time above 0 time (1/1/1900)
select @baseDate = '1 May 2005', @interval = '1901'
declare @timeRanges table (beginIntervalInclusive datetime, endIntervalExclusive datetime)
declare @i int
set @i = 1
while @i <= 10
begin
insert @timeRanges values(@baseDate, @baseDate + @interval)
set @baseDate = @baseDate + @interval
set @i = @i + 1
end
SELECT
tr.beginIntervalInclusive,
tr.endIntervalExclusive,
COUNT(*) AS cnt
FROM logins join @timeRanges as tr
on logins.date >= tr.beginIntervalInclusive
and logins.date < tr.endIntervalExclusive
GROUP BY tr.beginIntervalInclusive, tr.endIntervalExclusive
ORDER BY tr.beginIntervalInclusive
Otros consejos
Si puede encontrar una manera de definir sus rangos de fechas en una tabla separada, seleccione una etiqueta y dos columnas de fechas y únase a eso desde su consulta principal, algo como esto dependiendo de sus tablas.
Select Count(*) as NoLogons, DateRangeLabel
From logins a
inner join
(
Select
DateRangeLabel, StartDate, EndDate
From tblMyDates
) b
on a.date between b.startdate and b.enddate
Group by DateRangeLabel