我想获得一个构建一个列的表,该列跟踪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;
答案 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
子句。id
,因此您需要生成一系列interval
值和CROSS JOIN
以及id
值。这样就产生了21行。id
中intervals
s的出现次数。你可以subtract a date
from another date
给出你介于两者之间的天数。因此,从最早的日期减去行的日期,除以7得到间隔期,加1使interval
从1开始并转换为整数。然后,您可以转换&gt;的计数使用CASE
and coalesce()
。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