Postgres join on min(*) + group by super slow
Posted
技术标签:
【中文标题】Postgres join on min(*) + group by super slow【英文标题】: 【发布时间】:2020-08-28 01:14:00 【问题描述】:这是我的查询的样子:
SELECT DISTINCT user.id AS user_id, messaged_date
FROM users
JOIN (
SELECT MIN(message_date) AS messaged_date,
user_id
FROM messages
GROUP BY user_id
) messages_join ON user.id = messages_join.user_id
WHERE (user.client_id IN ('1234')
AND user.status IN ('statusA')
);
messages
表有两列 -> user_id
和 messaged_date
。我有一个索引和多列索引(user_id, messaged_data asc nulls last)
messages
桌子很大。大小约为 50Gb。
查询需要很长时间(5 分钟以上)才能运行。
如果我删除连接,它会在 1 秒内返回。
我想要每个user_id
一行
如何使查询执行得更快?显然,min
加上 group_by
的加入是原因。 (EXPLAIN
证实了这一点。)
我尝试了loose index scan, aka a "skip scan",它本身性能很好,但在加入它的结果时没有帮助。
【问题讨论】:
你想做什么? 获取用户收到消息的最早日期,在client_id
和 status
上过滤
这不是您的查询所做的......
我的错,我在选择子句中添加了缺少的messaged_date
【参考方案1】:
这解决了问题的原始版本。
子查询很奇怪。看起来你只是想要exists
:
SELECT u.id AS user_id
FROM users u
WHERE u.client_id IN ('1234') AND
u.status IN ('statusA') AND
EXISTS (SELECT 1
FROM messages m
WHERE m.user_id = u.id
);
这可以利用messages(user_id)
上的索引。
【讨论】:
这不会将消息折叠到最早的消息,min(messaged_date)
会这样做。
@ritratt 。 . .您没有使用最早的日期。
哎呀,我的错。我将列添加到选择中。【参考方案2】:
也许你想要这个:
SELECT DISTINCT ON (users.id)
users.id AS user_id,
messages.message_date AS messaged
FROM users
JOIN messages
ON users.id = messages.user_id
WHERE users.client_id = '1234'
AND users.status = 'statusA'
ORDER BY users.id, message.message_date;
可以加快查询速度的索引:
CREATE INDEX ON users (client_id, status);
CREATE INDEX ON messages (user_id);
【讨论】:
这看起来很有希望,但仍然需要很长时间 我修复了答案中的一些错误并添加了两个索引建议。 我已经有了messages (user_id)
索引。我还添加了users (client_id, status)
索引。但仍然需要 5 多分钟。
除非您将EXPLAIN (ANALYZE, BUFFERS)
输出添加到问题中,否则您无法获得更多信息。以上是关于Postgres join on min(*) + group by super slow的主要内容,如果未能解决你的问题,请参考以下文章
只有 JOIN 在 postgres 中意味着啥? [复制]
Postgres COUNT 个带有 INNER JOIN 的列值