I have the following simple left join query:
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'
Every _id and the h.hely_nev,h.hely_telepules is indexed and it runs under 0.0008 sec.
But if I add one more where clause (OR sz.szakma_id = 1) the speed drops down to 0.7 sec! this is really slow.
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
50k rows in helyek, eladok, eladok_rel_szakmak and only 30 rows in szakmak. I need to join all tables because i need some field from all.
The question is, how can i optimize the second query to perform better?
Here are the EXPLAINs:
This is the fast query:
+----+-------------+-------+-------------+------------------------------+------------------------------+---------+----------------+------+--------------------------------------------------------+
| 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 | |
+----+-------------+-------+-------------+------------------------------+------------------------------+---------+----------------+------+--------------------------------------------------------+
This is the slow:
+----+-------------+-------+--------+------------------------------+----------+---------+----------------+-------------+-------------+
| 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 |
+----+-------------+-------+--------+------------------------------+----------+---------+----------------+-------------+-------------+
I see the second query cant use any keys, but i dont know why ( there is an index on the sz.szakma_id field )
EDIT: I forgot to mention: I need to use more than one clause group. Like this:
(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)
Thats why i cant use two seperate queries. The goal is to search in the h.hely_nev, h.hely_telepules ,the sz.szakma_id fields for every word what the user enter in a search form. For example if the user enters "xyz" i need to select every record where the h.hely_nev equals to x or y or z and the h.hely_telepules equals to x or y or z and so on.
At the root of it, it is because in the first case, the query optimizer is able to use the indexes on helyek
to determine that there are only two possible candidate rows.
When you add the OR condition on szakmak
, you disallow the use of indexes on helvek
for purposes of narrowing down the potential result set. You would probably be best served to do a UNION of the results of two seperate queries, one with conditions:
WHERE h.hely_nev = 'xy'
OR h.hely_telepules = 'xy'
and another with condition
WHERE sz.szakma_id = 1
So something like:
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
You might also be able to utilize a series of right joins if you believe the szakmak
table with have less cardinality (and more rows for a given filter condition) than helyek
So you flip the query around like this:
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
This would change the table dependency order. I am not sure which would perform best for you.
See more information in MySQL documentation on LEFT/RIGHT JOIN optimization here:
http://dev.mysql.com/doc/refman/5.6/en/left-join-optimization.html
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.