Reputation: 5887
I'm trying to optimise the following query, but its running really slowly for me:
SELECT `trans_email`.*
, `email_statuses`.`recipient`, `email_statuses`.`status_id`, `email_statuses`.`message`, `email_statuses`.`status_received_at`
, `trans`.`doc`
FROM `trans_email`
LEFT JOIN `email_statuses` ON `trans_email`.`id` = `email_statuses`.`trans_email_id`
LEFT JOIN `trans` ON `trans_email`.`trans_id` = `trans`.`id`
WHERE `trans_email`.`type_id` = 0 AND `trans`.`company_id` = 1
ORDER BY `email_statuses`.`status_received_at` DESC
LIMIT 25 OFFSET 0
25 rows in set (4.87 sec)
Here's the output from the EXPLAIN
:
id: 1
select_type: SIMPLE
table: trans_email
partitions: NULL
type: ALL
possible_keys: trans_id
key: NULL
key_len: NULL
ref: NULL
rows: 769970
filtered: 10.00
Extra: Using where; Using temporary; Using filesort
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: trans
partitions: NULL
type: eq_ref
possible_keys: PRIMARY,fk_trans_company,co_del_drft_type,co_drft_del_utc
key: PRIMARY
key_len: 4
ref: trans_email.trans_id
rows: 1
filtered: 5.00
Extra: Using where
*************************** 3. row ***************************
id: 1
select_type: SIMPLE
table: email_statuses
partitions: NULL
type: ref
possible_keys: email_statuses_trans_email_id_foreign
key: email_statuses_trans_email_id_foreign
key_len: 4
ref: trans_email.id
rows: 2
filtered: 100.00
Extra: NULL
3 rows in set, 1 warning (0.00 sec)
As far as I can see, everything is correctly indexed. (Note that trans_email.type_id
is actually a bool, and has therefore not been indexed.)
Upvotes: 0
Views: 35
Reputation: 198388
You use LEFT JOIN though you require a non-NULL field in the joined table in your WHERE.
`trans`.`company_id` = 1
cannot be true if the LEFT JOIN produces a NULL row for trans
, so none of the extra rows produced by LEFT JOIN (compared to inner JOIN) would be admitted in the end result.
With a LEFT JOIN, you produce at least 769970 rows (i.e. at least one per row in trans_email
), then you trim them down to 25. If you had an inner JOIN instead, you would immediately cut down to 50ish rows (assuming roughly equal distribution of the boolean column) just from your primary index, then cut down to 25 with your boolean condition.
EDIT: Changing the other LEFT JOIN (the email_statuses
one) will actually change your results, unless you have full coverage in your email_statuses
table, and it shouldn't really affect your runtime that much, once the other LEFT JOIN is gone, so feel free to leave that LEFT JOIN as is.
Thus - try this one (only one word slimmer):
SELECT `trans_email`.*
, `email_statuses`.`recipient`, `email_statuses`.`status_id`, `email_statuses`.`message`, `email_statuses`.`status_received_at`
, `trans`.`doc`
FROM `trans_email`
JOIN `email_statuses` ON `trans_email`.`id` = `email_statuses`.`trans_email_id`
LEFT JOIN `trans` ON `trans_email`.`trans_id` = `trans`.`id`
WHERE `trans_email`.`type_id` = 0 AND `trans`.`company_id` = 1
ORDER BY `email_statuses`.`status_received_at` DESC
LIMIT 25 OFFSET 0
Upvotes: 1