I have a support ticketing system. I am in the process of adding in a departments section, where users can be members of multiple departments.
The issue is that I am using a subquery to get tickets with a department_id that is IN the department access table for that user.
This is the subquery:
t.department_id IN (SELECT utd.department_id FROM users_to_departments utd WHERE utd.user_id = :department_or_assigned_or_user_id AND utd.site_id = :site_id)
This is slowing the query down. It is taking about 2 seconds for a table of 110,000 tickets. The Subquery is the cause.
I tried converting it to a LEFT JOIN and then using HAVING utd.id IS NOT NULL, but the speed was worse.
I was wondering if I could convert it to an inner join.
The issue is that I always want to get tickets that are created by that user too, even if the ticket is now in a different department.
Currently using the following after the subquery todo this:
OR (t.assigned_user_id = :department_or_assigned_or_user_id OR t.user_id = :department_or_assigned_or_user_id)
All the correct columns are indexed, so MySQL isn’t doing any filesorts etc.
The users_to_departments table is simply user_id and department_id.
Any help would be awesome.
Here is my complete query.
SELECT
t.* ,
u.pushover_key AS `owner_pushover_key`,
u.name AS `owner_name`,
u.id AS `owner_id`,
u.email AS `owner_email`,
u.phone_number AS `owner_phone`,
u.email_notifications AS `owner_email_notifications`,
u2.pushover_key AS `assigned_pushover_key`,
u2.name AS `assigned_name`,
u2.id AS `assigned_id`,
u2.email AS `assigned_email`,
u2.email_notifications AS `assigned_email_notifications`,
u3.name AS `submitted_name`,
u3.id AS `submitted_id`,
u3.email AS `submitted_email`,
u3.email_notifications AS `submitted_email_notifications`,
tp.name AS `priority_name`,
td.name AS `department_name`,
ts.name AS `status_name`,
ts.colour AS `status_colour`,
ts.active AS `active`,
pa.name AS `pop_account_name`
FROM
tickets t
LEFT JOIN users u ON u.id = t.user_id
LEFT JOIN users u2 ON u2.id = t.assigned_user_id
LEFT JOIN users u3 ON u3.id = t.submitted_user_id
LEFT JOIN ticket_priorities tp ON tp.id = t.priority_id
LEFT JOIN ticket_departments td ON td.id = t.department_id
LEFT JOIN ticket_status ts ON ts.id = t.state_id
LEFT JOIN pop_accounts pa ON pa.id = t.pop_account_id
WHERE
1 = 1
AND t.site_id = :site_id
AND (
t.department_id IN (SELECT utd.department_id FROM users_to_departments utd WHERE utd.user_id = :department_or_assigned_or_user_id AND utd.site_id = :site_id)
OR
(t.assigned_user_id = :department_or_assigned_or_user_id OR t.user_id = :department_or_assigned_or_user_id)
)
ORDER BY
t.last_modified DESC
LIMIT :limit OFFSET :offset
Here is a link to the MySQL Explain results:
http://michaeldale.com.au/images/explain.html
enter code hereIf you are so sure you slowness is due to referred subquery, it seems your table users_to_departments should have an index based upon user_id + site_id fields.Furthermore, replacing an IN/NOT IN or EXISTS/NOT EXISTS by a LEFT JOIN combined with NULL or NOT NULL (on WHERE clause) constraint on joined table does indeed improve performance on SQL Server engines.
Well taking into account I am not fully aware on table contents I will just present you two suggestions (I you can use only Option 2 or combine both):
1 – I would split current query into two queries and join them into a single resultset using the clause UNION ALL (usually I also use the clause ALL to make any duplicates visible, since they often represent bad designed queries or inadeqaute database design)
2 – I would try to replace the IN subquery by a LEFT JOIN in order to assess if performance gains fits your need. Please notem that since the subquery may contains duplicates on [department_id], you may need need or not use the DISTINCT clause on top of your select.
Hope it helps