关于sqlgroupby的一个问题
我有一个名为visiting的表,如下所示:关于sqlgroupby的一个问题,sql,database,postgresql,Sql,Database,Postgresql,我有一个名为visiting的表,如下所示: 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 |
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查询。我只关心如何完成它:这两种方法中的一种或两种都可以工作?但是,这两种方法最终都会在结果中为您提供比您要求的更多的列
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;
这个问题有点模棱两可,因为您假设或要求时间从设定点开始,也就是说,自然查询也会表明08:58到09:58之间的所有访问的结果记录为1,2。您必须告诉您的查询,开始时间是出于某种可确定的原因访问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
今天早上,对于我脆弱的头脑来说,这种额外的逻辑将是昂贵而复杂的,有一个比我在博士后更好的人可能会解决这个问题
我通常希望通过在表中设置一个sessionkey列来解决这个问题,因为性能原因,我可以廉价地分组,但我认为还有一个逻辑问题。从计时中获取会话信息对我来说似乎很危险,因为我不相信用户在一个小时的活动后一定会注销。大多数会话系统的工作方式是在一段时间不活动后终止会话,也就是说,9:45之后的访问很可能会在同一个会话中,因为您的每小时周期将在9:08重置。这不能在单个SQL中完成。
更好的选择是在存储过程中处理它如果它是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中写这篇文章。这个问题似乎有点模糊 它变得更加复杂,因为id 3在id 1和2的一个小时内,但如果用户在9:50访问,那么这将是在id 2的一个小时内,而不是1 你似乎在追求一个平滑的总数——对于给定的访问,在接下来的一小时内有多少次访问 也许你应该问一下,在一个小时之内,你有多少次的访问?如果一次访问距离前一次不到一个小时,那么它应该“算数”吗 所以你可能想要的是你有多少个链接,链接少于任意数量,所以假设9:50的访问将包含在以id 1开始的链接中 没有简单的解决办法 在一个SQL语句中无法做到这一点。 下面是两个想法:一个使用循环计算访问次数,另一个更改访问表的填充方式 循环解 但是,使用循环可以轻松完成。 我已经试着让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
希望这有帮助。如果我犯了错误,请留下评论,我会纠正它。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;
是的,一个小时内是很难解释的,但根据给出的示例,似乎一个小时内就是他们想要的。坦白说,我认为从时间戳中提取会话是一个失败的主张,因此我的sessionkey专栏关于小时的陈述不是我想要的,我只是修改了示例数据。我不会说这些都非常有效,但我相信他们应该完成工作。是的,你明白我的意思,这正是我想要的。似乎很难使用sql,我正在考虑annakata提到的sessionkey方法…unsliced在描述方面比我更好:-是的,我认为如果可以的话,最好在数据库中植入所需的数据,而不是事后派生数据。因为第一种方法的sql查询很复杂,很难维护和扩展,我更喜欢第二种方法。谢谢
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;