在左连接表上使用 WHERE OR 时查询很慢?
Posted
技术标签:
【中文标题】在左连接表上使用 WHERE OR 时查询很慢?【英文标题】:Query is slow when use WHERE OR on tables left join? 【发布时间】:2020-10-24 16:50:16 【问题描述】:我有表 user
与表 A
和表 B
的关系 OneToMany。我需要在表 A
和表 B
上找到其合作伙伴 = 275 的用户
我的查询是:
SELECT u.id
FROM user u
LEFT JOIN A a
ON a.user_id = u.id
LEFT JOIN B b
ON b.user_id = u.id
WHERE a.partner_id = 275
OR b.partner_id = 275
GROUP BY u.id
ORDER BY u.id DESC
LIMIT 20
虽然我在表上创建索引,但查询速度很慢。 这里解释一下:
id : 1
select_type: SIMPLE
table: u
type: index
possible_keys: NULL
key: PRIMARY
key_len: 4
ref: NULL
rows: 20
Extra: Using index
我试图删除“或”侧,例如:
SELECT u.id FROM user u
LEFT JOIN A a ON a.user_id = u.id
LEFT JOIN B b ON b.user_id = u.id
WHERE a.partner_id = 275
GROUP BY u.id ORDER BY u.id DESC LIMIT 20
或
SELECT u.id FROM user u
LEFT JOIN A a ON a.user_id = u.id
LEFT JOIN B b ON b.user_id = u.id
WHERE b.partner_id = 275
GROUP BY u.id ORDER BY u.id DESC LIMIT 20
两个查询都很快。不知道为什么?
【问题讨论】:
请提供样本数据和期望的结果。 术语问题——你说“both”,但查询却说“OR”。是哪一个;它在查询制定和优化方面有很大的不同。 【参考方案1】:如果您想要 a
或 b
的合作伙伴为 275 的用户 ID,您可以使用:
SELECT a.user_id
FROM A a
WHERE a.partner_id = 275
UNION
SELECT b.user_id
FROM B b
WHERE b.partner_id = 275;
ORDER BY user_id DESC
LIMIT 20;
那么对于这个查询,您需要A(partner_id, user_id)
和B(partner_id, user_id)
上的索引。
【讨论】:
【参考方案2】:大概,OR
条件正在扼杀性能;这是 SQL 中经常遇到的问题。
您可以尝试使用两个exists
条件来表达这一点;这避免了外部聚合,并使意图更清晰:
select u.id
from user u
where exists (select 1 from a where a.user_id = u.id and a.partner_id = 275)
or exists (select 1 from b where b.user_id = u.id and b.partner_id = 275)
order by u.id desc limit 20
这会带来在a
或b
中与给定partner_id
匹配的用户。
为了提高性能,您需要以下索引:
a(partner_id, user_id)
b(partner_id, user_id)
您可以尝试更改每个索引中列的顺序,看看是否有区别。
【讨论】:
【参考方案3】:如果您在两个表上有索引(例如,在 partner_id 上),使用它们的简单方法是将 OR 更改为 UNION
或 UNION ALL
。例如
SELECT u.id
FROM user u
WHERE u.id IN
(SELECT a.user_id
FROM A a
WHERE a.partner_id = 275
UNION
SELECT b.user_id
FROM B b
WHERE b.partner_id = 275
)
ORDER BY u.id DESC
LIMIT 20;
为了便于阅读,我在这里使用了IN
,但您可以使用LEFT JOIN
,如果您愿意,甚至可以使用EXISTS
。
上述的优点是每个 SELECT 都可以专门命中索引 - 并且不会将查询计划生成与“或”混淆。
请注意,从技术上讲,您在这里也不需要 user
表(例如,您可以只使用 UNIONed 语句并从那里对 user_ids 进行排序 - 但我猜您可能还需要用户表中的其他信息.
【讨论】:
【参考方案4】:如果WHERE
条件从 JOIN 的 LEFT 侧引用列,则连接将转换为 INNER JOIN
。所以不需要LEFT JOIN
。因为您需要找到具有“partners = 275 on both table...”的行,所以不需要OR
条件。像这样。
select distinct u.id
from [user] u
join a a on a.user_id = u.id
join b b on b.user_id = u.id
where a.partner_id = 275
and b.partner_id = 275
order by u.id desc
limit 20;
据推测,user.id 上有一个索引。 a 和 b 表将受益于覆盖 (user_id, partner_id) 的索引
【讨论】:
我相信 OP 希望在 至少一个 表 a 或 b 中找到存在的 user_ids(与合作伙伴 275)。 user_ids 不需要同时存在于两者中,因此这些内部连接过于严格。 好吧,把另一只虾扔到芭比娃娃上,叫我鳄鱼邓迪!你看看那个。以上是关于在左连接表上使用 WHERE OR 时查询很慢?的主要内容,如果未能解决你的问题,请参考以下文章
实体框架在左连接时强制内连接使用 DefaultIfEmpty() 语法
SQL Server:在左连接查询的执行计划中插入隐藏的“排序”