我有两个表,一个包含帖子,另一个包含评论,有数百万个帖子和100个百万条评论,评论表还包含帖子的ID。评论在一段时间后停用,我想知道哪些帖子在被停用之前的30天内评论最多。
我要做的是从评论表中找到每个帖子的最大值(comment_date),并从每个帖子的那个日期起1个月后计算所有评论。
基本上我想按post_id
进行分组,找到max(comment_date)
并计算每个帖子max(comment_date) - 1 month
的所有评论。我正在努力创建查询以获取此数据?
数据库是postgres 9.4.1。
答案 0 :(得分:2)
在该数据量上,查询将花费时间。一种方法是使用窗口函数:
select post_id, count(*)
from (select c.*, max(comment_date) over (partition by post_id) as maxcd
from comments c
) c
where comment_date >= maxcd - interval '1 month'
group by post_id;
答案 1 :(得分:0)
我会尝试在这里使用LATERAL
加入。如果您在comments
上的(post_id, comment_date DESC)
表上创建索引,则可能比Gordon Linoff建议的变体更有效。这实际上取决于您的数据分布。可能没有必要在索引中指定DESC
,如果(post_id, comment_date ASC)
,优化程序可能足够聪明地使用它。但是,列的顺序很重要。
这是SQL Fiddle。
查询字面上遵循您在问题中概述的步骤。
我们扫描posts
表一次。对于每个帖子,我们会找到包含最新comment_date
的评论,并从中减去30天。这应该通过寻找索引来完成。然后我们会在找到的日期减去30天之后计算这篇文章的所有评论。这应该通过远程索引扫描来完成。
SELECT
posts.id
,c_count.post_count
FROM
posts
INNER JOIN LATERAL
(
SELECT comments.comment_date - interval '30 days' AS max_date
FROM comments
WHERE comments.post_id = posts.id
ORDER BY comments.comment_date DESC
LIMIT 1
) AS c_max_date ON true
INNER JOIN LATERAL
(
SELECT COUNT(*) AS post_count
FROM comments
WHERE
comments.post_id = posts.id
AND comments.comment_date >= c_max_date.max_date
) AS c_count ON true
;
可以使用窗口函数一次完成两个步骤(找到最大日期,然后在30天间隔内计算行数)。