我有一个名为 visit 的表,如下所示:
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
BTW,我使用postgresql 8.3。
谢谢!
更新:更新了示例数据表中的时间戳。抱歉混淆。
UPDATE :如果解决方案是单个sql查询,使用存储过程,子查询等,我不在乎。我只关心如何完成它:)
答案 0 :(得分:1)
这个问题有点含糊不清,因为你正在做出假设,或者要求小时数从一个设定点开始,即自然查询也会表明所有的结果都有(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上比我更好的人可以解决这个问题。
我通常希望通过在表格中有一个sessionkey列来解决这个问题,因为出于性能原因我可以便宜地分组,但我认为这也是一个逻辑问题。从时间中获取会话信息对我来说似乎很危险,因为我不相信用户在一小时活动之后肯定会被注销。大多数会话系统通过在不活动一段时间后使会话到期,即很可能在9:45之后的访问将在同一会话中,因为您的每小时时段将重置为9:08。
答案 1 :(得分:1)
问题似乎有点模糊。
它变得更复杂,因为id 3在id 1和2的一小时内,但如果用户在9:50访问那么那将是在1小时内但不是1小时。
你似乎完成了一个平滑的总数 - 对于一次特定的访问,在接下来的一小时内有多少次访问?
也许您应该询问访问不到一小时的访问次数是多少?如果访问距离前一个小时不到一个小时,那么它应该“计数”吗?
所以你可能想要的是你有多少链,其中链接少于任意数量(因此假设的9:50访问将包含在以id 1开头的链中)。
答案 2 :(得分:1)
在单个SQL语句中无法执行此操作
以下是2个想法:一个使用循环来计算访问次数,另一个使用循环来填充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
希望这会有所帮助。如果我犯了错误(我确信我有错误),请发表评论我会纠正错误。
答案 3 :(得分:1)
PostgreSQL 8.4将有一个窗口函数,到那时我们可以消除创建临时表只是为了模拟rownumbers(序列目的)
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;
答案 4 :(得分:0)
其中一个或两个可能有用吗?但是,两者最终会在结果中为您提供比您要求的更多列。
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;
答案 5 :(得分:0)
这不能在单个SQL中完成。 更好的选择是在存储过程中处理它
答案 6 :(得分:0)
如果是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中写这个。