在 WHERE 或 FROM 子句中进行子选择?
Posted
技术标签:
【中文标题】在 WHERE 或 FROM 子句中进行子选择?【英文标题】:Subselect in WHERE or FROM clause? 【发布时间】:2020-10-04 22:37:42 【问题描述】:如果特定子选择(子查询)位于WHERE
或FROM
子句中,我想知道查询的一般性能。我没有找到足够的解释哪种方式更好。在这种查询中我们应该如何应用子选择有一些规则吗?
我准备了下面的例子
查询自
SELECT name
FROM users a
JOIN (SELECT user_id, AVG(score) as score
FROM scores GROUP BY user_id
) b ON a.id=b.user_id
WHERE b.score > 15;
查询 WHERE
SELECT name
FROM users
WHERE
(SELECT AVG(score) as score
FROM scores WHERE scores.user_id=users.id GROUP BY user_id
) > 15;
表格:
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(30));
CREATE TABLE scores (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
score INT);
INSERT INTO users(name)
VALUES ('John'), ('Max'), ('Dan'), ('Alex');
INSERT INTO scores(user_id, score)
VALUES
(1, 20),
(1, 19),
(2, 15),
(2, 10),
(3, 20),
(3, 18),
(4, 13),
(4, 16),
(4, 15);
【问题讨论】:
您使用的是哪个 dbms? 你比较过解释/执行计划吗? 尝试运行这两个查询并选择最适合您的系统和数据的查询。 因为WHERE b.score...
,LEFT
不相关。请删除它。
@Tajni 。 . .有一般规则。而且我认为在许多情况下,相关子查询会更快。但我也可以想到例外。您正在谈论专门针对您的数据和系统提高性能;为此,您应该进行测试。
【参考方案1】:
在这两种情况下,scores
都需要 INDEX(user_id, score)
来提高性能。
很难预测哪个会跑得更快。
有时查询类似于第一个公式非常好。这是因为它不再关注 b
并一次有效地计算所有 AVG。然后它到达另一张桌子以获取最终信息。
让我们通过在WHERE
子句中添加一些其他测试来稍微调整第二个版本。现在第二个可能会更快。
这可能会更好:
SELECT name
FROM ( SELECT user_id -- Don't fetch AVG if not needed
FROM scores GROUP BY user_id
HAVING AVG(score) > 15; -- Note
) b
JOIN users a ON a.id = b.user_id
(FROM 和 JOIN 的交换不是优化,只是为了显示优化器执行步骤的顺序。)
在其他一些情况下,EXISTS( SELECT ... )
是有益的。 (但我看不到你的情况。
您的问题是关于一般优化。我想强调的是没有通用答案。
【讨论】:
我认为在第二个查询中分别为每个用户计算 AVG,所以它应该更慢。但是后来我读到一些 DBMS 优化了这种查询以一次计算所有查询,我想知道这个技巧的优势在哪里。谢谢你。我认为这是一个简单答案的问题。只需要知道 mysql 底层是如何工作的。 @Tajni - 一些简单的问题有简单的答案;有些没有。由于 MySQL 不断发展,一些答案会随着时间而改变。 MySQL 4.0(2 十年前)中不存在子查询。 5.6 在这方面增加了一些显着的性能增强。等等。【参考方案2】:我认为这个请求比你上面给出的要快,因为它没有子查询。
SELECT u.name
FROM users u
JOIN scores s
ON (s.user_id = u.id)
GROUP BY s.user_id
HAVING AVG(s.score) > 15
你可以在这个链接上看到它:http://sqlfiddle.com/#!9/b050f9/16 它显示了接下来 3 个 Select 查询的执行时间:
SELECT name
FROM users a
JOIN (SELECT user_id, AVG(score) as score
FROM scores GROUP BY user_id
) b ON a.id=b.user_id
WHERE b.score > 15;
SELECT name
FROM users
WHERE
(SELECT AVG(score) as score
FROM scores WHERE scores.user_id=users.id GROUP BY user_id
) > 15;
SELECT u.name
FROM users u
JOIN scores s
ON (s.user_id = u.id)
GROUP BY s.user_id
HAVING AVG(s.score) > 15
【讨论】:
以上是关于在 WHERE 或 FROM 子句中进行子选择?的主要内容,如果未能解决你的问题,请参考以下文章