2009-05-06 10 views
3

J'ai des données de connexion utilisateur avec horodatage et ce que je voudrais faire est obtenir un histogramme des connexions par année, mais avec l'année commençant à une date arbitraire. Par exemple, je veux le genre d'information suivants:Histogramme basé sur la date non standard SQL Server

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 

Les étiquettes dans la première colonne ne sont pas importants, mais les plages de dates sont. Je sais que je peux le briser par des années détroit avec:

SELECT YEAR([date]) AS [year], COUNT(*) AS cnt 
FROM logins 
GROUP BY YEAR([date]) 
ORDER BY [year] 

Mais cela ne me donne pas les plages de données que je veux. Comment cela peut-il être fait?

Répondre

3
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]) 

EDIT - Désolé, vous avez raison. Voici une version fixe (je l'ai utilisé une table de test pour commencer ...)

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 

Modifiez les unités de datediff si vous voulez plus de granularité jours. EDIT # 2 - ok, voici une solution plus robuste qui gère les années bissextiles :) EDIT # 3 - En fait, cela ne gère pas les années bissextiles, mais permet de spécifier des intervalles de temps variables. Aller avec dateadd (année, 1, @baseDate) pour l'approche de l'année bissextile sécuritaire.

declare @baseDate datetime, @interval datetime 
[email protected] 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 
+0

Donne exactement les mêmes résultats, juste sans l'année en avant. – cdeszaq

+0

@cdeszaq - vous avez raison, désolé à ce sujet. La requête est maintenant fixée, j'aurais dû utiliser une table de test pour commencer .. – ahains

+0

:) Je venais tout juste de m'en occuper moi-même ... mais va-t-il gérer correctement les années bissextiles, ou cela va-t-il introduire des erreurs? – cdeszaq

1

Si vous pouvez trouver un moyen de définir vos plages de dates dans un tableau séparé, sélectionnez une étiquette et deux colonnes de dates et joignez-vous à celle de votre requête principale quelque chose comme ceci en fonction de vos tables.

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 
Questions connexes