我怎样才能使这个 sql 查询更快?

How can I make this sql query faster?

我有一个 table user_notifications,它有 1100000 条记录,我必须 运行 下面的查询,但完成查询需要 3 分钟多的时间,我该怎么办缩短获取时间。

SELECT `user_notifications`.`user_id`
FROM `user_notifications`
WHERE `user_notifications`.`notification_template_id` = 175
AND (DATE(sent_at) >= DATE_SUB(CURDATE(), INTERVAL 4 day))
AND `user_notifications`.`user_id` IN (
  1203, 1282, 1499, 2244, 2575, 2697, 2828, 2900, 3085, 3989,
  5264, 5314, 5368, 5452, 5603, 6133, 6498..
)

IN块中的用户id有时高达1k。

为了优化,我在 user_notification table.

中的 user_idnotification_template_id 列建立了索引

您查询的日期范围似乎很小。有一个基于 SENT_AT 列的索引怎么样?您知道当前查询使用的是什么索引吗?

大 IN() 列表本质上很慢。创建一个带有索引的临时 table 并将 IN() 列表中的值放入该临时 table 中,然后您将获得索引连接的强大功能,而不是巨大的 IN() 列表。

(1) 如果您可能需要使用索引,请不要在函数中隐藏列:

AND (DATE(sent_at) >= DATE_SUB(CURDATE(), INTERVAL 4 day))

-->

AND sent_at >= CURDATE() - INTERVAL 4 day

(2) 使用 "composite" 索引

WHERE `notification_template_id` = 175
  AND sent_at >= ...
  AND `user_id` IN (...)

第一列应该是带有“=”的那一列。不清楚接下来要放什么,所以我建议添加这两个索引:

INDEX(notification_template_id, user_id, sent_at)
INDEX(notification_template_id, sent_at)

优化器可能会在它们之间正确选择。

复合索引与单个列上的索引不同

(3) 是的,您 可以 尝试将 IN 列表放入 tmp table,但这样做的成本可能超过收益。我不认为 IN() 中的 1K 值是 "too many"。

(4) My cookbook 关于建立索引。