2013-12-16 91 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