时间间隔的计数列

时间:2015-07-20 23:51:30

标签: postgresql

我想获得一个构建一个列的表,该列跟踪id在给定周内出现的次数。如果id出现一次,如果它出现两次,如果它出现两次,则给出一个2,但如果它出现两次以上,则给出一个0。

id  date 
a   2015-11-10
a   2015-11-25
a   2015-11-09
b   2015-11-10
b   2015-11-09
a   2015-11-05
b   2015-11-23
b   2015-11-28
b   2015-12-04
a   2015-11-10
b   2015-12-04
a   2015-12-07
a   2015-12-09
c   2015-11-30
a   2015-12-06
c   2015-10-31
c   2015-11-04
b   2015-12-01
a   2015-10-30
a   2015-12-14

一周的间隔如下:

1 -  2015-10-30  to  2015-11-05
2 -  2015-11-06  to  2015-11-12
3 -  2015-11-13  to  2015-11-19
4 -  2015-11-20  to  2015-11-26
5 -  2015-11-27  to  2015-12-03
6 -  2015-12-04  to  2015-12-10
7 -  2015-12-11  to  2015-12-17

表格应如下所示。

id  interval  count
 a    1         2
 b    1         0
 c    1         2
 a    2         0
 b    2         2
 c    2         0
 a    3         0
 b    3         0
 c    3         0
 a    4         1
 b    4         1
 c    4         0
 a    5         0
 b    5         2
 c    5         1
 a    6         0
 b    6         2
 c    6         0
 a    7         1
 b    7         0
 c    7         0

间隔栏不一定要在那里,为了清楚起见,我只是添加了它。

我是sql的新手,我不确定如何将日期分成间隔。我唯一拥有的是按日期和计数分组。

 Select id ,date, count (*) as frequency 
 from data_1 
 group by id, date  having  frequency <= 2;

2 个答案:

答案 0 :(得分:1)

只关注您提供的数据,这就是诀窍:

SELECT v.id, 
       i.interval, 
       coalesce((CASE WHEN sub.cnt < 3 THEN sub.cnt ELSE 0 END), 0) AS count
FROM (VALUES('a'), ('b'), ('c')) v(id)
CROSS JOIN generate_series(1, 7) i(interval)
LEFT JOIN (
  SELECT id, ((date - '2015-10-30')/7 + 1)::int AS interval, count(*) AS cnt
  FROM my_table
  GROUP BY 1, 2) sub USING (id, interval)
ORDER BY 2, 1;

几句解释:

  • 您有三个id值,此处使用VALUES clause重新创建。如果您还有更多或者事先不知道要枚举哪个ID,您始终可以使用子查询替换VALUES子句。
  • 您提供超过7周的特定日期范围。由于您可能有几周没有某个id,因此您需要生成一系列interval值和CROSS JOIN以及id值。这样就产生了21行。
  • 然后计算idintervals s的出现次数。你可以subtract a date from another date给出你介于两者之间的天数。因此,从最早的日期减去行的日期,除以7得到间隔期,加1使interval从1开始并转换为整数。然后,您可以转换&gt;的计数使用CASE and coalesce()
  • 的组合将2到0和NULL设置为0
  • 查询也会输出interval,否则您将无法确定数据所指的内容。或者,您可以将其转换为显示间隔日期范围的列。

更灵活的解决方案

如果您有更多id秒和更大的日期范围,则可以使用以下版本,该版本首先确定不同的id和日期范围。请注意,interval现在从0开始,以便于计算。并不重要,因为不是间隔号,而是显示相应的日期范围。

WITH mi AS (
  SELECT min(date) AS min, ((max(date) - min(date))/7)::int AS intv FROM my_table)
SELECT v.id, 
       to_char((mi.min + i.intv * 7)::timestamp, 'YYYY-mm-dd') || ' - ' || 
       to_char((mi.min + i.intv * 7 + 6)::timestamp, 'YYYY-mm-dd') AS period, 
       coalesce((CASE WHEN sub.cnt < 3 THEN sub.cnt ELSE 0 END), 0) AS count
FROM mi,
     (SELECT DISTINCT id FROM my_table) v
CROSS JOIN LATERAL generate_series(0, mi.intv) i(intv)
LEFT JOIN LATERAL (
  SELECT id, ((date - mi.min)/7)::int AS intv, count(*) AS cnt
  FROM my_table
  GROUP BY 1, 2) sub USING (id, intv)
ORDER BY 2, 1;

SQLFiddle两种解决方案。

答案 1 :(得分:0)

假设您有一个包含所有用户的表,这将有所帮助。

select 
    users.id,
    interval_table.id,
    CASE
        WHEN count(log_table.user_id)>2 THEN 0
        ELSE count(log_table.user_id)
    END
from users
cross join interval_table
left outer join log_table
    on users.id = log_table.user_id
   and log_table.event_date >= interval_table.start_interval
   and log_table.event_date < interval_table.stop_interval
group by users.id, interval_table.id
order by interval_table.id, users.id

检查出来:http://sqlfiddle.com/#!15/1a822/21