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_idmessaged_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_idstatus 上过滤 这不是您的查询所做的...... 我的错,我在选择子句中添加了缺少的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-xl left join 执行时间过长

Postgres JOIN 与 unnest

Postgres COUNT 个带有 INNER JOIN 的列值

在 Postgres JOIN 查询中区分 null 和 empty

SQL / Postgres join where子句