1

哪个查询执行得更快,哪个是完美查询?

SELECT
    COUNT(*) AS count
FROM
    students
WHERE
    status = 1
AND
    classes_id IN(
                    SELECT
                        id
                    FROM
                        classes
                    WHERE
                        departments_id = 1
                );

或者

SELECT
    COUNT(*) AS count
FROM
    students s
LEFT JOIN
    classes c
ON
    c.id = s.classes_id
WHERE
    status = 1
AND
    c.departments_id = 1

我已经放置了两个查询都将输出相同的结果。现在我想知道哪种方法执行得更快,哪种方法是正确的方法?

4

6 回答 6

5

您应该始终使用EXPLAIN来确定您的查询将如何运行。

不幸的是,MySQL 会将您的子查询作为 DEPENDENT QUERY 执行,这意味着将为外部查询中的每一行运行子查询。你会认为 MySQL 足够聪明,可以检测到子查询不是相关的子查询,并且只会运行一次,唉,它还没有那么聪明。

因此,MySQL 将扫描学生中的所有行,为每一行运行子查询,并且不使用外部查询上的任何索引。

将查询编写为 JOIN 将允许 MySQL 使用索引,以下查询将是编写它的最佳方式:

SELECT COUNT(*) AS count
FROMstudents s
JOIN classes c
  ON c.id = s.classes_id
  AND c.departments_id = 1
WHERE s.status = 1

这将使用以下索引:

students(`status`)
classes(`id`, `departements_id`) : multi-column index
于 2012-05-21T15:11:07.737 回答
3

从设计和清晰度的角度来看,我会避免像第一个那样的内部选择。确实,要 100% 确定是否或如何优化每个查询以及哪个会“更好”运行,需要了解您使用的 SQL 服务器将如何解释它及其计划。在 Mysql 中,使用“解释”。

但是....即使没有看到这一点,我的钱仍然在仅加入版本上...内部选择版本必须在确定要在“IN”子句中使用的值之前完整地执行内部选择-我当您将东西包装在函数中时知道这是真的,并且非常肯定当将选择作为 IN 争论时这是真的。我也知道这是完全抵消内部选择中的表上的索引可能带来的任何好处的好方法。

我通常认为只有非常罕见的查询情况才真正需要内部选择。通常,那些使用它们的人经常像传统的迭代流程序员一样思考,而不是真正思考关系数据库结果集术语......

于 2012-05-21T11:38:07.517 回答
2

分别解释两个查询

两个查询之间的区别在于子查询与连接

大多数情况下,连接比子查询快。Join 创建执行计划并预测将要处理的数据,因此可以节省时间。另一方面,子查询运行所有查询,直到加载所有数据。大多数开发人员使用子查询,因为它们比 JOIN 更具可读性,但在性能很重要的地方,JOIN 是更好的解决方案。

于 2012-05-21T11:39:35.823 回答
2

找出答案的最好方法是测量它:

无索引

  • 查询一:0.9s
  • 查询2:0.9s

有索引

  • 查询一:0.4s
  • 查询2:0.2s

结论是:

  • 如果您没有索引,那么您使用哪个查询没有区别。
  • 如果您有正确的索引,连接会更快。
  • 添加正确索引的效果大于选择正确查询的效果。如果性能很重要,请确保您拥有正确的索引。

当然,您的结果可能会因 MySQL 版本和您拥有的数据分布而异。

这是我测试它的方法:

  • 1,000,000 名学生(25% 处于状态 1)。
  • 50,000 门课程。
  • 10个部门。

这是我用来创建测试数据的 SQL:

CREATE TABLE students
(id INT PRIMARY KEY AUTO_INCREMENT,
status int NOT NULL,
classes_id int NOT NULL);

CREATE TABLE classes
(id INT PRIMARY KEY AUTO_INCREMENT,
departments_id INT NOT NULL);

CREATE TABLE numbers(id INT PRIMARY KEY AUTO_INCREMENT);

INSERT INTO numbers VALUES (),(),(),(),(),(),(),(),(),();

INSERT INTO numbers
SELECT NULL
FROM numbers AS n1
CROSS JOIN numbers AS n2
CROSS JOIN numbers AS n3
CROSS JOIN numbers AS n4
CROSS JOIN numbers AS n5
CROSS JOIN numbers AS n6;

INSERT INTO classes (departments_id)
SELECT id % 10 FROM numbers WHERE id <= 50000;

INSERT INTO students (status, classes_id)
SELECT id % 4 = 0, id % 50000 + 1 FROM numbers WHERE id <= 1000000;

SELECT COUNT(*) AS count
FROM students
WHERE status = 1
AND classes_id IN (SELECT id FROM classes WHERE departments_id = 1);

SELECT COUNT(*) AS count
FROM students s
LEFT JOIN classes c
ON c.id = s.classes_id
WHERE status = 1
AND c.departments_id = 1;

CREATE INDEX ix_students ON students(status, classes_id);
于 2012-06-13T22:40:50.780 回答
1

这两个查询不会产生相同的结果:

SELECT
    COUNT(*) AS count
FROM
    students
WHERE
    status = 1
AND
    classes_id IN(
                    SELECT
                        id
                    FROM
                        classes
                    WHERE
                        departments_id = 1
                );

...将返回学生表中具有 classes_id 字段的行数,该字段也在 classes 表中,并且部门 ID 为 1。

SELECT
    COUNT(*) AS count
FROM
    students s
LEFT JOIN
    classes c
ON
    c.id = s.classes_id
WHERE
    status = 1
AND
    c.departments_id = 1

...将返回状态字段为 1 的学生表中的总行数,并且可能更多,具体取决于您的数据的组织方式。

如果您希望查询返回相同的内容,则需要将 LEFT JOIN 更改为 INNER JOIN,以便它只匹配同时满足这两个条件的行。

于 2012-06-16T20:59:08.253 回答
0

在两个查询上运行EXPLAIN SELECT ...并检查哪一个在做什么;)

于 2012-05-21T11:27:16.047 回答