Reputation: 5196
I have a mysql query like
SELECT `tbl_ticket`.`id`, `tbl_ticket`.`hd_user_username`,
`tbl_ticket`.`hd_user_email`, `tbl_ticket`.`ticket_title`,
`tbl_complain_type`.`complains` FROM `tbl_ticket` LEFT JOIN
`tbl_ticket_complain` ON tbl_ticket_complain.ticket_id=tbl_ticket.id
LEFT JOIN `tbl_complain_type` ON tbl_complain_type.id=tbl_ticket_complain.complain_id
LEFT JOIN `tbl_assignment` ON tbl_assignment.ticket_id=tbl_ticket.id
WHERE ((((`hd_user_username` LIKE '%searchterm%')
AND (`tbl_assignment`.`id` IN ($array)))
OR (`hd_user_email`='searchterm'))
OR (`ticket_title`='searchterm')) OR (`tbl_complain_type`.`complains`='searchterm')
$array contains around 7000 values like `$array=array(1,2,3,..)`
This query takes around 8 seconds to execute. Is there any alternative solution for this query ? The value of $array is got from another query
select max(id) from tbl_assignment group by ticket_id
The slowness of query is due to multiple joins between tables
Upvotes: 3
Views: 73
Reputation: 1269743
This is basically your query:
SELECT . . .
FROM tbl_ticket t LEFT JOIN
tbl_ticket_complain tc
ON tc.ticket_id = t.id LEFT JOIN
tbl_complain_type tct
ON tct.id = tc.complain_id LEFT JOIN
tbl_assignment a
ON a.ticket_id = t.id
WHERE (((hd_user_username LIKE '%searchterm%' AND
a.id IN ($array)
) OR
`hd_user_email`='searchterm'
) OR
ticket_title = 'searchterm'
) OR
tct.complain` = 'searchterm';
The issue with performance has nothing to do with IN
. In fact, MySQL optimizes IN
, as explained in the documentation:
If all values are constants, they are evaluated according to the type of expr and sorted. The search for the item then is done using a binary search. This means
IN
is very quick if theIN
value list consists entirely of constants.
You are not going to get faster than an IN
list with constants.
The problem with your query is the string of OR
s. These make is almost impossible for the optimizer to use indexes -- so the full result set has to be created and then filtered down.
It is hard for me to see how to improve this in your query. Sometimes, splitting a query into simpler chunks and connecting them using union
or union all
does the trick. Your conditions are a bit hard to follow, making that approach difficult for an outsider.
Upvotes: 0
Reputation: 133360
If the values in the array use in you IN clause come from a select you could use the fact that
An IN clause is equivalent to an inner join so you could use a inner join between your_table_with_id and the table.column you need for match eg:
SELECT `
tbl_ticket`.`id`
, `tbl_ticket`.`hd_user_username`
, `tbl_ticket`.`hd_user_email`
, `tbl_ticket`.`ticket_title`
, `tbl_complain_type`.`complains`
FROM `tbl_ticket`
LEFT JOIN `tbl_ticket_complain` ON tbl_ticket_complain.ticket_id=tbl_ticket.id
LEFT JOIN `tbl_complain_type` ON tbl_complain_type.id=tbl_ticket_complain.complain_id
LEFT JOIN `tbl_assignment` ON tbl_assignment.ticket_id=tbl_ticket.id
INNER JOIN your_table_with_id ON `tbl_assignment`.`id` = JOIN your_table_with_id.id
WHERE ((((`hd_user_username` LIKE '%searchterm%')
OR (`hd_user_email`='searchterm'))
OR (`ticket_title`='searchterm')) OR (`tbl_complain_type`.`complains`='searchterm')
Remeber also that the content of values use IN clause is limited and fail when the limit is exceeded
and in your case
SELECT `
tbl_ticket`.`id`
, `tbl_ticket`.`hd_user_username`
, `tbl_ticket`.`hd_user_email`
, `tbl_ticket`.`ticket_title`
, `tbl_complain_type`.`complains`
FROM `tbl_ticket`
LEFT JOIN `tbl_ticket_complain` ON tbl_ticket_complain.ticket_id=tbl_ticket.id
LEFT JOIN `tbl_complain_type` ON tbl_complain_type.id=tbl_ticket_complain.complain_id
LEFT JOIN `tbl_assignment` ON tbl_assignment.ticket_id=tbl_ticket.id
INNER JOIN (
select max(id) as id
from tbl_assignment
group by ticket_id
) t ON `tbl_assignment`.`id` = t.id
WHERE ((((`hd_user_username` LIKE '%searchterm%')
OR (`hd_user_email`='searchterm'))
OR (`ticket_title`='searchterm')) OR (`tbl_complain_type`.`complains`='searchterm'))
Upvotes: 3