我有以下简单的左连接查询:
SELECT SQL_NO_CACHE * FROM helyek h
LEFT JOIN eladok e ON e.elado_id = h.elado_id
LEFT JOIN eladok_rel_szakmak ersz ON ersz.elado_id = e.elado_id
LEFT JOIN szakmak sz ON sz.szakma_id = ersz.szakma_id
WHERE h.hely_nev = 'xy'
OR h.hely_telepules = 'xy'
每一个id和h.hely_nev,h.hely_telepules都被索引,它运行在0.0008秒以下。
但如果我再加上一个where子句(或sz.szakma_id=1),速度就会下降到0.7秒!这真的很慢。
SELECT SQL_NO_CACHE * FROM helyek h
LEFT JOIN eladok e ON e.elado_id = h.elado_id
LEFT JOIN eladok_rel_szakmak ersz ON ersz.elado_id = e.elado_id
LEFT JOIN szakmak sz ON sz.szakma_id = ersz.szakma_id
WHERE h.hely_nev = 'xy'
OR h.hely_telepules = 'xy'
OR sz.szakma_id = 1
赫尔耶克、埃拉多克、埃拉多克雷尔扎克马克有5万排,扎克马克只有30排。我需要加入所有的表格,因为我需要一些所有字段。
问题是,如何优化第二个查询以更好地执行?
以下是解释:
这是快速查询:
+----+-------------+-------+-------------+------------------------------+------------------------------+---------+----------------+------+--------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------------+------------------------------+------------------------------+---------+----------------+------+--------------------------------------------------------+
| 1 | SIMPLE | h | index_merge | idxhelynev,idxhely_telepules | idxhelynev,idxhely_telepules | 482,482 | NULL | 2 | Using union(idxhelynev,idxhely_telepules); Using where |
| 1 | SIMPLE | e | eq_ref | PRIMARY | PRIMARY | 4 | h.elado_id | 1 | |
| 1 | SIMPLE | ersz | ref | elado_id | elado_id | 4 | e.elado_id | 1 | |
| 1 | SIMPLE | sz | eq_ref | PRIMARY | PRIMARY | 4 | ersz.szakma_id | 1 | |
+----+-------------+-------+-------------+------------------------------+------------------------------+---------+----------------+------+--------------------------------------------------------+
这就是慢:
+----+-------------+-------+--------+------------------------------+----------+---------+----------------+-------------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+------------------------------+----------+---------+----------------+-------------+-------------+
| 1 | SIMPLE | h | ALL | idxhelynev,idxhely_telepules | NULL | NULL | NULL | 54326 | |
| 1 | SIMPLE | e | eq_ref | PRIMARY | PRIMARY | 4 | h.elado_id | 1 | |
| 1 | SIMPLE | ersz | ref | elado_id | elado_id | 4 | e.elado_id | 1 | |
| 1 | SIMPLE | sz | eq_ref | PRIMARY | PRIMARY | 4 | ersz.szakma_id | 1 | Using where |
+----+-------------+-------+--------+------------------------------+----------+---------+----------------+-------------+-------------+
我看到第二个查询不能使用任何键,但我不知道为什么(sz.szakma_id字段上有一个索引)
编辑:我忘了说:我需要使用多个子句组。这样地:
(h.hely_nev = 'x' OR h.hely_telepules = 'x' OR sz.szakma_id = x)
AND
(h.hely_nev = 'y' OR h.hely_telepules = 'y' OR sz.szakma_id = y)
AND
(h.hely_nev = 'z' OR h.hely_telepules = 'z' OR sz.szakma_id = z)
这就是为什么我不能使用两个独立的查询。
目标是在h.hely_nev、h.hely_telepules和sz.szakma_id字段中搜索用户在搜索表单中输入的每个单词。
例如,如果用户输入“x y z”,我需要选择h.hely_nev等于x或y或z,h.hely_telepules等于x或y或z的每个记录,依此类推。
最佳答案
从根本上说,这是因为在第一种情况下,查询优化器能够使用helyek
上的索引来确定只有两个可能的候选行。
在szakmak
上添加OR条件时,不允许使用helvek
上的索引来缩小潜在的结果集。最好是将两个独立查询的结果联合起来,其中一个查询有以下条件:
WHERE h.hely_nev = 'xy'
OR h.hely_telepules = 'xy'
另一个有条件的
WHERE sz.szakma_id = 1
比如说:
SELECT SQL_NO_CACHE * FROM helyek h
LEFT JOIN eladok e ON e.elado_id = h.elado_id
LEFT JOIN eladok_rel_szakmak ersz ON ersz.elado_id = e.elado_id
LEFT JOIN szakmak sz ON sz.szakma_id = ersz.szakma_id
WHERE h.hely_nev = 'xy'
OR h.hely_telepules = 'xy'
UNION DISTINCT
SELECT SQL_NO_CACHE * FROM helyek h
LEFT JOIN eladok e ON e.elado_id = h.elado_id
LEFT JOIN eladok_rel_szakmak ersz ON ersz.elado_id = e.elado_id
LEFT JOIN szakmak sz ON sz.szakma_id = ersz.szakma_id
WHERE sz.szakma_id = 1
如果您认为
szakmak
表的基数比helyek
小(对于给定的筛选条件,行数比多),那么还可以使用一系列右联接所以您可以这样翻转查询:
SELECT SQL_NO_CACHE *
FROM
szakmak sz
RIGHT JOIN eladok_rel_szakmak ersz ON sz.szakma_id = ersz.szakma_id
RIGHT JOIN eladok e ON ersz.elado_id = e.elado_id
RIGHT JOIN helyek h ON e.elado_id = h.elado_id
WHERE h.hely_nev = 'xy'
OR h.hely_telepules = 'xy'
OR sz.szakma_id = 1
这将更改表依赖关系顺序。我不确定哪个对你最好。
有关左/右连接优化的详细信息,请参见MySQL文档:
http://dev.mysql.com/doc/refman/5.6/en/left-join-optimization.html