2013-12-16 61 views
1

我有以下简单的左连接查询:mysql的奇怪表现异常加入

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子句(OR 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 

在helyek,eladok,eladok_rel_szakmak和szakmak只有30行50k行。我需要加入所有表格,因为我需要一些场地。

问题是,我如何优化第二个查询来执行更好?

这里有解释:

这是快速查询:

+----+-------------+-------+-------------+------------------------------+------------------------------+---------+----------------+------+--------------------------------------------------------+ 
| 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等等。

+0

使用2查询,而不是1 - 太多或公司会杀了 – matino

回答

4

它的根源在于,在第一种情况下,查询优化器能够使用helyek上的索引来确定只有两个可能的候选行。

当您在szakmak上添加OR条件时,您不允许使用helvek上的索引来缩小潜在结果集。你很可能会提供最好的服务做两个不同的查询,其中一个条件的结果的UNION:

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文档在左/右这里JOIN优化:

http://dev.mysql.com/doc/refman/5.6/en/left-join-optimization.html

+0

谢谢你,对不起性能:(我忘了提及:我需要使用多个子句组(编辑该问题) – user974250

+0

@ user974250这最终会变成一个非常丑陋的查询。对我来说,似乎您可能希望查看您的模式以获得解决方案。过滤这样一个复杂的方式记录,我想知道是否有更好的方式来关联这些行。从你的例子看来,你正在寻找识别案例e行必须在三个字段中具有三个可能的值中的一个,但每个记录在每个位置都必须具有唯一值。 (有点像一个智力游戏)。这是因为您使用AND来加入这些过滤条件。 –

+0

是的,这是正确的。这就是为什么我需要用AND加入条件,这就是为什么我不能分离条件。 – user974250