哪个查询执行速度更快,哪个查询更完美?
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
我已经放置了两个查询,它们都会输出相同的结果。现在我想知道哪种方法执行速度更快,哪种方法更正确?
答案 0 :(得分: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
答案 1 :(得分:3)
从设计和清晰度的角度来看,我会避免像第一个那样的内在选择。确实,100%确定每个查询是否或如何优化以及哪个将运行“更好”需要了解您正在使用的SQL服务器将如何与其进行相互作用以及它的计划。在Mysql中,使用“Explain”。
然而....即使没有看到这一点,我的钱仍然只在加入版本...内部选择版本必须在确定要在“IN”子句中使用的值之前完整地执行内部选择 - 当你在函数中包装东西时我知道这是真的,并且当把一个选择作为IN争论时非常确定它是真的。我也知道,这是一种很好的方法,可以完全抵消内部选择内部表格上的索引所带来的任何好处。
我一般认为内部选择只是非常罕见的查询情况才真正需要。通常,那些使用它们的人经常会像传统的迭代流程序员一样思考,而不是真正考虑关系数据库结果集术语......
答案 2 :(得分:2)
EXPLAIN 两个查询单独
两个查询之间的差异是子查询与联接
主要是联接比子查询更快。 Join创建执行计划并预测要处理的数据,从而节省时间。另一方面,子查询运行所有查询,直到加载所有数据。大多数开发人员使用子查询,因为它们比JOINS更具可读性,但是在性能很重要的情况下,JOIN是更好的解决方案。
答案 3 :(得分:2)
找出答案的最佳方法是衡量它:
没有索引
使用索引
结论是:
当然,您的结果可能会有所不同,具体取决于MySQL版本和您拥有的数据分布。
以下是我测试它的方式:
这是我用来创建测试数据的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);
答案 4 :(得分:1)
这两个查询不会产生相同的结果:
SELECT
COUNT(*) AS count
FROM
students
WHERE
status = 1
AND
classes_id IN(
SELECT
id
FROM
classes
WHERE
departments_id = 1
);
...将返回students表中具有classes_id字段的行数,该字段也位于classes_id为1的classes表中。
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,以便它只匹配适合这两种情况的行。
答案 5 :(得分:0)
对两个查询运行EXPLAIN SELECT ...
并检查哪一个做了什么;)