我正在尝试优化以下查询,但它对我来说运行得很慢:
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)
这是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)
据我所知,一切都被正确索引。(请注意,trans_email.type_id
实际上是一个布尔值,因此尚未编制索引。
您可以使用 LEFT JOIN,尽管您需要在 WHERE 的连接表中有一个非 NULL 字段。
如果 LEFT JOIN 为trans
生成一个 NULL 行,则`trans`.`company_id` = 1
不能为 true,因此 LEFT JOIN 产生的任何额外行(与内部 JOIN 相比(都不会在最终结果中被接纳。
使用 LEFT JOIN,您至少生成 769970 行(即trans_email
中每行至少一行(,然后将它们修剪为 25 行。如果你有一个内部 JOIN,你会立即从你的主索引减少到 50 行(假设布尔列的分布大致相等(,然后根据布尔条件减少到 25 行。
编辑:更改另一个左联接(email_statuses
一个(实际上会改变你的结果,除非你的email_statuses
表中有完整的覆盖,一旦另一个左联接消失,它应该不会真正影响你的运行时,所以请随意保留左联接原样。
因此 - 试试这个(只有一个词更瘦(:
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