优化查询以从不同的表中获取唯一(用户)记录
Posted
技术标签:
【中文标题】优化查询以从不同的表中获取唯一(用户)记录【英文标题】:Optimize query to get unique (user) records from different tables 【发布时间】:2013-02-12 11:56:32 【问题描述】:我已经让这个查询完美运行,但问题是当我的 4 个表变得太大时,它变得很慢。
如何优化?
SELECT
all_records.user_id,
users.NAME,
users.IMAGE
FROM (
SELECT user_id FROM comments
WHERE commentable_id = #object.id
AND commentable_type = '#object.class.to_s'
UNION ALL
SELECT user_id FROM hello
WHERE helloable_id = #object.id
AND helloable_type = '#object.class.to_s'
UNION ALL
SELECT user_id FROM foo
WHERE fooable_id = #object.id
AND fooable_type = '#object.class.to_s'
UNION ALL
SELECT user_id FROM bar
WHERE barable_id = #object.id
AND barable_type = '#object.class.to_s'
) AS all_records
INNER JOIN users ON users.id = all_records.user_id
GROUP BY
all_records.user_id,
users.NAME,
users.IMAGE
LIMIT 15
查询应该做的是获取对 (4) 表执行操作的唯一用户(请原谅表名称的更改)。即使使用LIMIT 15
,它仍然运行缓慢,因为我认为它仍然可以读取所有 4 个表。我这样做是对的还是有什么方法可以优化它?
供参考:我正在使用 postgres 并使用 rails 但在 find_by_sql
中执行它。
编辑
本地 postgres:9.0.5; heroku postgres:9.1
【问题讨论】:
正如我在 IRC 上的回应 - 运行解释分析,并显示输出。最好粘贴在explain.depesz.com。 这是解释中显示的内容explain.depesz.com/s/3uZ3 @index: 这只是EXPLAIN
。我们需要EXPLAIN ANALYZE
首先,总是,您的 PostgreSQL 版本是什么?其次,必不可少:您想要任意 15 行选择、真正随机 选择还是所有 行?第一种情况是迄今为止最便宜的。
解释表明 tt 没有“读取所有 4 个表” - 即它不对它们进行 seq 扫描。它使用索引扫描从所有 4 个表中读取数据。如果您不需要来自所有 4 个表的数据,为什么将它们包含在查询中?另外 - 说明 analyze 是关键,在某些情况下,版本信息也会有所帮助。
【参考方案1】:
照原样回答您的问题:“获取 15 行任意行”。那应该非常快。
SELECT u.id, u.name, u.image
FROM (
SELECT id
FROM (
SELECT user_id AS id
FROM comments
WHERE commentable_id = #object.id
AND commentable_type = '#object.class.to_s'
UNION ALL
SELECT user_id
FROM hello
WHERE helloable_id = #object.id
AND helloable_type = '#object.class.to_s'
UNION ALL
SELECT user_id
FROM foo
WHERE fooable_id = #object.id
AND fooable_type = '#object.class.to_s'
UNION ALL
SELECT user_id
FROM bar
WHERE barable_id = #object.id
AND barable_type = '#object.class.to_s'
) AS a
GROUP BY id
LIMIT 15
) b
JOIN users u USING (id)
如果您运行的是 PostgreSQL 9.1 或更高版本,则可以简化为 GROUP BY id
,假设 users.id
是主键。但我采取了更激进的方法。
我拉起GROUP BY
和LIMIT
一个查询级别,希望能够在基表上实现更快的索引扫描。使用LIMIT 15
而没有ORDER BY
则不应发生顺序扫描。 Postgres 可以从索引顶部读取元组,并在达到限制时立即停止。
与此密切相关的案例类似:Way to try multiple SELECTs till a result is available?
只有在这里 Postgres 从索引中读取元组。
你 might
使用 LEFT JOIN users
而不是 JOIN
(而不是我的额外子查询级别)可以达到相同的效果,因为 JOIN
可能会从结果中删除行并禁用更简单的查询计划。
为了获得完美的性能,你有像
这样的索引CREATE INDEX comments_mult_idx
ON comments (commentable_id, commentable_type, user_id)
在所有 4 张桌子上。 user_id
必须是最后一列。 Here's why.
【讨论】:
太棒了!谢谢。所以我在这个查询上做了一个explain analyze
并使用相同的对象进行挖掘,我从我的和你的得到Total runtime: 12.493 ms
和Total runtime: 1.663 ms
。可以使用来自explain analyze
的Total runtime
来确定查询的速度吗?
@index: 是的,EXPLAIN ANALYZE
为您提供了 Postgres 为您的查询(不包括网络开销)花费的实时时间(而不仅仅是 EXPLAIN,它只显示计划者的估计)。您可能需要运行几次以使缓存饱和。第二次或第三次运行通常会快一些。 Read the manual for details!
谢谢!这工作完美。 :) 你能解释一下我做了什么让查询这么慢吗?是因为join
吗?
这个查询可以实现更简单的计划(就像我在上面和链接的答案中试图解释的那样)。比较两者的EXPLAIN ANALYZE
的输出应该会提供更多详细信息。以上是关于优化查询以从不同的表中获取唯一(用户)记录的主要内容,如果未能解决你的问题,请参考以下文章