2008-10-03 7 views
4

J'ai une liste de fois dans une colonne de base de données (représentant des visites sur un site Web).Requête SQL pour la fréquence cumulée de la liste des dates

Je dois les regrouper par intervalles, puis obtenir une table de 'fréquence cumulée' de ces dates.

Par exemple, je pourrais avoir:

9:01 
9:04 
9:11 
9:13 
9:22 
9:24 
9:28 

et je veux convertir en

9:05 - 2 
9:15 - 4 
9:25 - 6 
9:30 - 7 

Comment puis-je faire cela? Puis-je même facilement réaliser cela en SQL? Je peux très facilement le faire en C#

Répondre

1

Créer une table periods décrivant les périodes que vous souhaitez diviser le jour en.

SELECT periods.name, count(time) 
    FROM periods, times 
WHERE period.start <= times.time 
    AND     times.time < period.end 
GROUP BY periods.name 
+0

Cela ne donnerait pas les totaux cumulatifs qui a été demandé, laissant tomber la mention « period.start <= times.time » réaliserait cela, comme ma réponse a montré . – ManiacZX

+0

True. J'ai mal lu la question. – ephemient

3

Je tiens à souligner que, d'après l'énoncé « intention » du problème, de faire une analyse sur le trafic des visiteurs - je l'ai écrit cette déclaration pour résumer les comptes en groupes homogènes. Faire autrement (comme dans les groupes "exemple") serait de comparer les comptes pendant un intervalle de 5 minutes à des comptes dans un intervalle de 10 minutes - ce qui n'a pas de sens.

Vous devez vous fier à «l'intention» de l'exigence de l'utilisateur, et non à la «lecture» littérale de celui-ci. :-)

create table #myDates 
     (
     myDate  datetime 
     ); 
    go 

    insert into #myDates values ('10/02/2008 09:01:23'); 
    insert into #myDates values ('10/02/2008 09:03:23'); 
    insert into #myDates values ('10/02/2008 09:05:23'); 
    insert into #myDates values ('10/02/2008 09:07:23'); 
    insert into #myDates values ('10/02/2008 09:11:23'); 
    insert into #myDates values ('10/02/2008 09:14:23'); 
    insert into #myDates values ('10/02/2008 09:19:23'); 
    insert into #myDates values ('10/02/2008 09:21:23'); 
    insert into #myDates values ('10/02/2008 09:21:23'); 
    insert into #myDates values ('10/02/2008 09:21:23'); 
    insert into #myDates values ('10/02/2008 09:21:23'); 
    insert into #myDates values ('10/02/2008 09:21:23'); 
    insert into #myDates values ('10/02/2008 09:26:23'); 
    insert into #myDates values ('10/02/2008 09:27:23'); 
    insert into #myDates values ('10/02/2008 09:29:23'); 
    go 

    declare @interval int; 
    set @interval = 10; 

    select 
     convert(varchar(5), dateadd(minute,@interval - datepart(minute, myDate) % @interval, myDate), 108) timeGroup, 
     count(*) 
    from 
     #myDates 
    group by 
     convert(varchar(5), dateadd(minute,@interval - datepart(minute, myDate) % @interval, myDate), 108) 

retuns: 

timeGroup    
--------- ----------- 
09:10  4   
09:20  3   
09:30  8   
+0

non cumulatif ... – KristoferA

+0

Oui, raté cette partie. :-) Ron –

+0

... mais la phrase "Vous devez grok à l'intention" de l'exigence de l'utilisateur, pas la "lecture" littérale de celui-ci. mérite presque un upvote ... :) – KristoferA

0

Il utilise un bon quelques astuces SQL (SQL Server 2005):

CREATE TABLE [dbo].[stackoverflow_165571](
    [visit] [datetime] NOT NULL 
) ON [PRIMARY] 
GO 

;WITH buckets AS (
    SELECT dateadd(mi, (1 + datediff(mi, 0, visit - 1 - dateadd(dd, 0, datediff(dd, 0, visit)))/5) * 5, 0) AS visit_bucket 
      ,COUNT(*) AS visit_count 
    FROM stackoverflow_165571 
    GROUP BY dateadd(mi, (1 + datediff(mi, 0, visit - 1 - dateadd(dd, 0, datediff(dd, 0, visit)))/5) * 5, 0) 
) 
SELECT LEFT(CONVERT(varchar, l.visit_bucket, 8), 5) + ' - ' + CONVERT(varchar, SUM(r.visit_count)) 
FROM buckets l 
LEFT JOIN buckets r 
    ON r.visit_bucket <= l.visit_bucket 
GROUP BY l.visit_bucket 
ORDER BY l.visit_bucket 

Notez qu'il met tous les temps le même jour, et suppose qu'ils sont dans une colonne datetime . La seule chose qu'il ne fait pas comme votre exemple est de supprimer les zéros en tête de la représentation temporelle.

1

Créez une table contenant les intervalles auxquels vous souhaitez obtenir des totaux, puis joignez les deux tables ensemble.

Tels que:

time_entry.time_entry 
----------------------- 
2008-10-02 09:01:00.000 
2008-10-02 09:04:00.000 
2008-10-02 09:11:00.000 
2008-10-02 09:13:00.000 
2008-10-02 09:22:00.000 
2008-10-02 09:24:00.000 
2008-10-02 09:28:00.000 

time_interval.time_end 
----------------------- 
2008-10-02 09:05:00.000 
2008-10-02 09:15:00.000 
2008-10-02 09:25:00.000 
2008-10-02 09:30:00.000 

SELECT 
    ti.time_end, 
    COUNT(*) AS 'interval_total' 
FROM time_interval ti 
INNER JOIN time_entry te 
    ON te.time_entry < ti.time_end 
GROUP BY ti.time_end; 


time_end    interval_total 
----------------------- ------------- 
2008-10-02 09:05:00.000 2 
2008-10-02 09:15:00.000 4 
2008-10-02 09:25:00.000 6 
2008-10-02 09:30:00.000 7 

Si au lieu de vouloir les totaux cumulatifs que vous vouliez les totaux dans une plage, puis vous ajoutez une colonne de TIME_START à la table time_interval et de modifier la requête

SELECT 
    ti.time_end, 
    COUNT(*) AS 'interval_total' 
FROM time_interval ti 
INNER JOIN time_entry te 
    ON te.time_entry >= ti.time_start 
      AND te.time_entry < ti.time_end 
GROUP BY ti.time_end; 
8
create table accu_times (time_val datetime not null, constraint pk_accu_times primary key (time_val)); 
go 

insert into accu_times values ('9:01'); 
insert into accu_times values ('9:05'); 
insert into accu_times values ('9:11'); 
insert into accu_times values ('9:13'); 
insert into accu_times values ('9:22'); 
insert into accu_times values ('9:24'); 
insert into accu_times values ('9:28'); 
go 

select rounded_time, 
    (
    select count(*) 
    from accu_times as at2 
    where at2.time_val <= rt.rounded_time 
    ) as accu_count 
from (
select distinct 
    dateadd(minute, round((datepart(minute, at.time_val) + 2)*2, -1)/2, 
    dateadd(hour, datepart(hour, at.time_val), 0) 
) as rounded_time 
from accu_times as at 
) as rt 
go 

drop table accu_times 

Résultats en:

rounded_time   accu_count 
----------------------- ----------- 
1900-01-01 09:05:00.000 2 
1900-01-01 09:15:00.000 4 
1900-01-01 09:25:00.000 6 
1900-01-01 09:30:00.000 7 
2

ooh, bien trop compliqué tout ça.

Normaliser secondes, diviser par votre intervalle de seau, tronquer et remultiply:

select sec_to_time(floor(time_to_sec(d)/300)*300), count(*) 
from d 
group by sec_to_time(floor(time_to_sec(d)/300)*300) 

Utilisation des données de Ron Savage, je reçois

+----------+----------+ 
| i  | count(*) | 
+----------+----------+ 
| 09:00:00 |  1 | 
| 09:05:00 |  3 | 
| 09:10:00 |  1 | 
| 09:15:00 |  1 | 
| 09:20:00 |  6 | 
| 09:25:00 |  2 | 
| 09:30:00 |  1 | 
+----------+----------+ 

Vous pouvez utiliser Ceil() ou ronde () au lieu de plancher().

Mise à jour: pour une table créée avec

create table d (
    d datetime 
); 
+0

Oui, mais qu'en est-il de ces périodes qui n'avaient pas d'entrées? S'il n'y a pas d'entrées dans un laps de temps spécifique de 5 minutes, j'aimerais voir un 0 compter là. Comment pouvez-vous y parvenir? – dubek

+0

Vous avez besoin d'une table avec les intervalles de temps souhaités sur une journée. A gauche, rejoignez-le avec la colonne sec_to_time ci-dessus. Vous obtenez les comptes avec coalesce (count (*), 0). Je pense que la suggestion de @ ManiacZX fait cela. – dland

Questions connexes