Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
关于sqlgroupby的一个问题_Sql_Database_Postgresql - Fatal编程技术网

关于sqlgroupby的一个问题

关于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 |

我有一个名为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 |          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;