我认为您根本不需要进行子查询:
SELECT D.name AS deptname, D.id AS deptid, WT1.sortposition AS deptsortposition,
C.name AS classname, C.id AS classid, C.department,
WT2.sortposition AS classsortposition, WT2.deptid
FROM departments AS D
JOIN web_taxonomy AS WT1 ON (WT1.deptid=D.id AND WT1.classid=0)
LEFT OUTER JOIN web_taxonomy AS WT2 ON (WT2.deptid=D.id AND WT2.subclassid=0)
LEFT OUTER JOIN classes AS C ON (C.id=WT2.classid AND C.department=WT2.deptid);
WT1 的连接应该受益于索引:
ALTER TABLE web_taxonomy
ADD KEY wt_dept_class (deptid, classid),
ADD KEY wt_dept_subclass (deptid, subclassid);
与类的连接使用该表的 PRIMARY 键索引。
这是此查询的 EXPLAIN 输出:
+----+-------------+-------+--------+--------------------------+---------------+---------+------------------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+--------------------------+---------------+---------+------------------+------+-----------------------+
| 1 | SIMPLE | D | ALL | PRIMARY,id | NULL | NULL | NULL | 1 | NULL |
| 1 | SIMPLE | WT1 | ref | dept_class,dept_subclass | dept_class | 10 | test.D.id,const | 1 | Using index condition |
| 1 | SIMPLE | WT2 | ref | dept_class,dept_subclass | dept_subclass | 10 | test.D.id,const | 1 | Using where |
| 1 | SIMPLE | C | eq_ref | PRIMARY,id | PRIMARY | 8 | test.WT2.classid | 1 | Using where |
+----+-------------+-------+--------+--------------------------+---------------+---------+------------------+------+-----------------------+
说实话,我不得不稍微编辑一下 EXPLAIN 报告以显示该结果。我用零行的表进行了测试,所以优化器错误地为 WT2 选择了 dept_class 索引。如果您使用真实数据进行测试,我认为它会正确选择 dept_subclass 索引。
我尝试了您的查询,并稍作修改以解决 D.deptid 上的错误:
SELECT *
FROM(
SELECT D.name AS deptname,D.id AS deptid,WT.sortposition AS deptsortposition
FROM departments D JOIN web_taxonomy WT ON (WT.deptid=D.id AND WT.classid=0)
WHERE D.web=1
ORDER BY sortposition
) AS D
LEFT JOIN (
SELECT C.name AS classname,C. id AS classid,C.department,WT.sortposition AS classsortposition,WT.deptid
FROM classes C
JOIN web_taxonomy WT ON (WT.classid=C.id AND WT.subclassid=0 AND WT.deptid=C.department)
WHERE web=1 ORDER BY classsortposition
) AS C ON (C.department=D.deptid);
和解释报告:
+----+-------------+------------+------+--------------------------+------------+---------+-----------------------------+------+----------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+------+--------------------------+------------+---------+-----------------------------+------+----------------------------------------------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 2 | NULL |
| 1 | PRIMARY | <derived3> | ALL | NULL | NULL | NULL | NULL | 2 | Using where; Using join buffer (Block Nested Loop) |
| 3 | DERIVED | C | ALL | PRIMARY,id | NULL | NULL | NULL | 1 | Using where; Using temporary; Using filesort |
| 3 | DERIVED | WT | ref | dept_class,dept_subclass | dept_class | 10 | test.C.department,test.C.id | 1 | Using index condition; Using where |
| 2 | DERIVED | D | ALL | PRIMARY,id | NULL | NULL | NULL | 1 | Using where; Using temporary; Using filesort |
| 2 | DERIVED | WT | ref | dept_class,dept_subclass | dept_class | 10 | test.D.id,const | 1 | Using index condition |
+----+-------------+------------+------+--------------------------+------------+---------+-----------------------------+------+----------------------------------------------------+
呸!它为部门和类(即ALL
类型列中的)运行表扫描,并为每个子查询创建一个临时表,然后在没有任何索引好处的情况下连接它们(这就是 的含义Using join buffer
)。不是一个有趣的优化计划。
一般来说,排序应该是 SQL 查询的最后一部分。不要尝试对子查询结果进行排序以绕过优化器。如果可能,您希望连接由索引辅助,但连接的最佳索引的顺序不一定是您希望返回最终结果的顺序。所以让优化器完成连接的工作,然后对最终结果进行排序。
SELECT D.name AS deptname, D.id AS deptid, WT1.sortposition AS deptsortposition,
C.name AS classname, C.id AS classid, C.department,
WT2.sortposition AS classsortposition, WT2.deptid
FROM departments AS D
JOIN web_taxonomy AS WT1 ON (WT1.deptid=D.id AND WT1.classid=0)
LEFT OUTER JOIN web_taxonomy AS WT2 ON (WT2.deptid=D.id AND WT2.subclassid=0)
LEFT OUTER JOIN classes AS C ON (C.id=WT2.classid AND C.department=WT2.deptid)
ORDER BY deptsortposition, classsortposition;
这仍然使用一个临时表和文件排序,但不是两个。它避免了连接缓冲区;每个连接都是索引辅助的。
+----+-------------+-------+--------+--------------------------+---------------+---------+------------------+------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+--------------------------+---------------+---------+------------------+------+---------------------------------+
| 1 | SIMPLE | D | ALL | PRIMARY,id | NULL | NULL | NULL | 1 | Using temporary; Using filesort |
| 1 | SIMPLE | WT1 | ref | dept_class,dept_subclass | dept_class | 10 | test.D.id,const | 1 | Using where; Using index |
| 1 | SIMPLE | WT2 | ref | dept_subclass | dept_subclass | 10 | test.D.id,const | 1 | Using where |
| 1 | SIMPLE | C | eq_ref | PRIMARY,id | PRIMARY | 8 | test.WT2.classid | 1 | Using where |
+----+-------------+-------+--------+--------------------------+---------------+---------+------------------+------+---------------------------------+