SQL-запрос для кумулятивной частоты списка datetime - PullRequest
4 голосов
/ 03 октября 2008

У меня есть список раз в столбце базы данных (представляющих посещения веб-сайта).

Мне нужно сгруппировать их по интервалам, а затем получить таблицу «кумулятивной частоты» этих дат.

Например, я мог бы иметь:

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

и я хочу преобразовать это в

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

Как я могу это сделать? Могу ли я даже легко достичь этого в SQL? Я могу довольно легко сделать это в C #

Ответы [ 6 ]

8 голосов
/ 03 октября 2008
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

Результат:

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
3 голосов
/ 03 октября 2008

Я должен отметить, что на основе заявленного «намерения» проблемы провести анализ трафика посетителей - я написал это утверждение, чтобы подвести итоги по единым группам.

В противном случае (как в группах «примеров») сравнивались бы значения в течение 5-минутного интервала с результатами в 10-минутном интервале - что не имеет смысла.

Вы должны придерживаться «намерения» требования пользователя, а не его буквального «чтения». : -)

    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           
2 голосов
/ 03 октября 2008

ох, слишком сложная вся эта штука.

Нормализовать до секунд, разделить на интервал сегмента, усечь и перемножить:

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)

Используя данные Рона Сэвиджа, я получаю

+----------+----------+
| 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 |
+----------+----------+

Вы можете использовать ceil () или round () вместо floor ().

Обновление: для таблицы, созданной с помощью

create table d (
    d datetime
);
1 голос
/ 03 октября 2008

Создайте таблицу, содержащую интервалы, по которым вы хотите получать итоги, затем объедините две таблицы.

Например:

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

Если вместо того, чтобы получать кумулятивные итоги, вы хотите итоги в пределах диапазона, то вы добавляете столбец time_start в таблицу time_interval и меняете запрос на

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;
1 голос
/ 03 октября 2008

Создайте таблицу periods, описывающую периоды, на которые вы хотите разделить день.

SELECT periods.name, count(time)
  FROM periods, times
 WHERE period.start <= times.time
   AND                 times.time < period.end
 GROUP BY periods.name
0 голосов
/ 03 октября 2008

При этом используется немало хитростей 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

Обратите внимание, что он помещает все время в один и тот же день и предполагает, что они находятся в столбце datetime. Единственное, что он не делает в качестве примера, это убирает начальные нули из представления времени.

...