вопрос про sql group by - PullRequest
       15

вопрос про sql group by

2 голосов
/ 06 января 2009

У меня есть таблица с именем посещение , которая выглядит следующим образом:

id | visitor_id | visit_time 
-------------------------------------
 1 |          1 | 2009-01-06 08:45:02 
 2 |          1 | 2009-01-06 08:58:11
 3 |          1 | 2009-01-06 09:08:23 
 4 |          1 | 2009-01-06 21:55:23
 5 |          1 | 2009-01-06 22:03:35

Я хочу разработать sql, который может узнать, сколько раз пользователь посещает за один сеанс (интервал последовательного посещения менее 1 часа).

Итак, для данных примера я хочу получить следующий результат:

visitor_id | count
-------------------
         1 |     3
         1 |     2

Кстати, я использую postgresql 8.3. Спасибо!

ОБНОВЛЕНИЕ : обновлены метки времени в таблице данных примера. извините за путаницу.
ОБНОВЛЕНИЕ : Меня не волнует, является ли решение одним SQL-запросом, использует процедуру хранилища, подзапрос и т. Д. Меня интересует только, как это сделать :)

Ответы [ 7 ]

1 голос
/ 06 января 2009

нет простого решения

Нет способа сделать это в одной SQL-статистике.
Ниже приведены две идеи: одна использует цикл для подсчета посещений, другая меняет способ заполнения таблицы visiting.

петлевое решение

Однако это можно сделать без особых проблем с помощью цикла.
(Я пытался получить правильный синтаксис postgresql, но я не эксперт)

/* find entries where there is no previous entry for */ 
/* the same visitor within the previous hour:        */ 

select v1.* , 0 visits 
into temp_table
from visiting v1
where not exists ( select 1 
                   from   visiting v2
                   where  v2.visitor_id = v1.visitor_id 
                   and    v2.visit_time < v1.visit_time 
                   and    v1.visit_time - interval '1 hour' <     v2.visit_time 
                 )  
select @rows = @@rowcount 

while @rows > 0 
begin
    update temp_table
    set    visits = visits + 1 , 
           last_time = v.visit_time 
    from   temp_table t , 
           visiting   v 
    where  t.visitor_id = v.visitor_id 
    and    v.visit_time - interval '1 hour' < t.last_time
    and    not exists ( select 1 
                        from   visiting v2 
                        where  v2.visitor_id = t.visitor_id 
                        and    v2.visit_time between t.last_time and v.visit_time 
                      ) 

    select @rows = @@rowcount 
end

/* get the result: */ 

select visitor_id, 
       visits 
from temp_table 

Идея состоит в том, чтобы сделать это:

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

лучшее решение?

Я предлагаю:

  • добавить столбец в таблицу visiting: session_id int not null
  • изменить процесс, который делает записи, чтобы он проверял, был ли предыдущий визит текущего посетителя менее часа назад. Если это так, он устанавливает session_id на то же значение, что и session id для этого более раннего посещения. Если нет, то генерируется новый session_id .
  • Вы можете включить эту логику в триггер.

Тогда ваш исходный запрос может быть решен с помощью:

SELECT session_id, visitor_id, count(*)
FROM   visiting 
GROUP BY session_id, visitor_id

Надеюсь, это поможет. Если я допустил ошибки (я уверен, что есть), оставьте комментарий, и я исправлю его.

1 голос
/ 06 января 2009

PostgreSQL 8.4 будет иметь функцию управления окнами, и к тому времени мы сможем исключить создание временной таблицы только для имитации чисел (последовательность)

create table visit
(
visitor_id int not null,
visit_time timestamp not null
);




insert into visit(visitor_id, visit_time) 
values
(1, '2009-01-06 08:45:02'),
(2, '2009-02-06 08:58:11'),
(1, '2009-01-06 08:58:11'),
(1, '2009-01-06 09:08:23'),
(1, '2009-01-06 21:55:23'),
(2, '2009-02-06 08:59:11'),
(2, '2009-02-07 00:01:00'),
(1, '2009-01-06 22:03:35');




create temp table temp_visit(visitor_id int not null, sequence serial not null, visit_time timestamp not null);
insert into temp_visit(visitor_id, visit_time) select visitor_id, visit_time from visit order by visitor_id, visit_time;


select 
    reference.visitor_id, count(nullif(reference.visit_time - prev.visit_time < interval '1 hour',false))
from temp_visit reference
left join temp_visit prev 
on prev.visitor_id = reference.visitor_id and prev.sequence = reference.sequence - 1
group by reference.visitor_id;
1 голос
/ 06 января 2009

Проблема кажется немного нечеткой.

Ситуация усложняется, поскольку идентификатор 3 находится в пределах часа от идентификатора 1 и 2, но если бы пользователь посетил в 9:50, то это было бы в течение часа 2, а не 1.

Похоже, что после сглаженной суммы - сколько посещений в течение следующего часа для данного посещения?

Возможно, вам следует спросить, сколько посещений имеет следующий визит менее чем за час? Если посещение прошло менее часа от предыдущего, то должно ли оно считаться?

Итак, что вы, вероятно, хотите, - это сколько у вас цепочек, если количество ссылок меньше произвольной суммы (поэтому гипотетическое посещение в 9:50 будет включено в цепочку, которая начинается с идентификатора 1).

1 голос
/ 06 января 2009

Вопрос немного двусмысленный, потому что вы делаете предположение или требуете, чтобы часы начинались с заданной точки, т.е. естественный запрос также будет указывать, что для всех результатов есть запись результата (1,2). визиты между часами 08:58 и 09:58. Вам нужно будет «сообщить» вашему запросу, что время начала по каким-то определенным причинам посещений 1 и 4, или вы получите естественный набор результатов:

visitor_id | count 
--------------------
         1 | 3
         1 | 2 <- extra result starting at visit 2
         1 | 1 <- extra result starting at visit 3
         1 | 2
         1 | 1 <- extra result starting at visit 5

Эта дополнительная логика будет дорогой и слишком сложной для моего хрупкого разума этим утром, кто-то лучше меня в postgres может решить эту проблему.

Обычно я хотел бы решить эту проблему, имея в таблице столбец сессионного ключа, по которому я мог бы дешево группировать по причинам производительности, но я думаю, что есть и логическая проблема. Получение информации о сеансе из таймингов мне кажется опасным, потому что я не верю, что пользователь определенно выйдет из системы после нескольких часов активности . Большинство систем сессий работают, заканчивая сеанс после периода неактивности , т.е. очень вероятно, что посещение после 9:45 будет в том же сеансе, потому что ваш почасовой период будет сброшен на 9 : 08.

0 голосов
/ 06 января 2009

Если бы это был T-SQL, я бы написал что-то вроде:

SELECT  visitor_id, COUNT(id), 
        DATEPART(yy, visit_time), DATEPART(m, visit_time), 
        DATEPART(d, visit_time), DATEPART(hh, visit_time)
FROM visiting
GROUP BY
    visitor_id, 
    DATEPART(yy, visit_time), DATEPART(m, visit_time), 
    DATEPART(d, visit_time), DATEPART(hh, visit_time)

, что дает мне:

1   3   2009    1   6   8
1   2   2009    1   6   21

Я не знаю, как и если вы можете написать это в postgre, хотя.

0 голосов
/ 06 января 2009

Этого нельзя сделать за один SQL. Лучший вариант - обработать его в хранимой процедуре

0 голосов
/ 06 января 2009

Один или оба из них могут работать? Однако оба результата приведут к тому, что в результате вы получите больше столбцов, чем вы просите.

SELECT visitor_id,
       date_part('year', visit_time),
       date_part('month', visit_time),
       date_part('day', visit_time),
       date_part('hour', visit_time),
       COUNT(*)
  FROM visiting
 GROUP BY 1, 2, 3, 4, 5;


SELECT visitor_id,
       EXTRACT(EPOCH FROM visit_time)-(EXTRACT(EPOCH FROM visit_time) % 3600),
       COUNT(*)
  FROM visiting
 GROUP BY 1, 2;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...