使用LEFT OUTER JOIN
并尝试使用Postgres右表中的列时,我遇到了严重的性能问题。我有一个用户表和一个包含online_users的表,其中列出了我网站上在线的用户ID。两个表都有用户ID中的索引。我需要在users表上运行select并列出第一个在线用户,然后是不在线的用户。所以我的选择是:
SELECT *
FROM users
LEFT JOIN online_users ON (users.id = online_users.usr_id)
ORDER BY online_users.online_date
我在users.id
,online_users.usr_id
和online_users.online_date
上有索引,但出于某种原因,当我在查询上运行ANALYZE
时,online_users.online_date
的索引Postgres不使用它,完整扫描会破坏查询的性能。
有没有办法在不更改表结构的情况下优化此查询(这些表被复制,因此更改结构将需要对项目进行重大重构)。
Postgre版本是9.3
以下是Explain Analyze:
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------
Sort (cost=2589440.94..2595456.84 rows=2406361 width=506) (actual time=18635.686..25775.334 rows=2239030 loops=1)
Sort Key: usuarios_online.datamessenger
Sort Method: external merge Disk: 512424kB
-> Hash Left Join (cost=219.73..130113.66 rows=2406361 width=506) (actual time=0.723..12388.266 rows=2239030 loops=1)
Hash Cond: (usuarios.id = usuarios_online.id_usr)
-> Seq Scan on usuarios (cost=0.00..108832.61 rows=2406361 width=494) (actual time=0.009..7328.191 rows=2238984 loops=1)
-> Hash (cost=212.66..212.66 rows=566 width=12) (actual time=0.704..0.704 rows=572 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 27kB
-> Seq Scan on usuarios_online (cost=0.00..212.66 rows=566 width=12) (actual time=0.079..0.555 rows=572 loops=1)
Total runtime: 28519.611 ms
(10 rows)
答案 0 :(得分:1)
由于您只从online_users
订购行,因此使用UNION
查询是有意义的:
(
SELECT usr_id, online_date -- more columns?
FROM online_users
ORDER BY online_date
)
UNION ALL
SELECT u.id, NULL -- more matching columns?
FROM users u
LEFT JOIN online_users o ON u.id = o.usr_id
WHERE o.usr_id IS NULL;
在任何情况下都应该快得多。
online_users
现在可以轻松利用online_date
上的索引
两个更简单的查询计划通常可以更容易地使用索引
所有其他用户根本不需要进行排序。第二个SELECT
只需排除online_users
:
第一个SELECT
周围的括号是必需的,以便我放置ORDER BY
。
这可能会进一步优化,具体取决于未声明的具体情况。