在 WHERE 或 FROM 子句中进行子选择?

Posted

技术标签:

【中文标题】在 WHERE 或 FROM 子句中进行子选择?【英文标题】:Subselect in WHERE or FROM clause? 【发布时间】:2020-10-04 22:37:42 【问题描述】:

如果特定子选择(子查询)位于WHEREFROM 子句中,我想知道查询的一般性能。我没有找到足够的解释哪种方式更好。在这种查询中我们应该如何应用子选择有一些规则吗?

我准备了下面的例子

查询自

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 子句中进行子选择?的主要内容,如果未能解决你的问题,请参考以下文章

更新子查询(WHERE/FROM)

Mysql在where子句中优化子查询

where条件放在子SQL语句中是否查询速度更快?

SQL在where子句中使用子选择中的列

SQL中的子查询

mysql子查询在where in子句中