我有一个关于mysql如何优化查询的问题。
Mark as JavaScript
Mysql在时间戳上使用全索引扫描,附带两个子查询。
我不明白为什么它首先不在时间戳上使用索引范围扫描,然后再应用子查询。
这有什么特别的原因吗? 如果没有,我可以强制它使用索引范围扫描吗?
答案 0 :(得分:1)
SELECT p.*
FROM pp AS p
LEFT JOIN Temp AS t ON (p.from = t.col) -- leading up to `OR`
LEFT JOIN Temp2 AS t2 ON (p.to = t2.col)
WHERE p.timestamp >= '2016-01-01'
AND p.timestamp < '2017-01-03' -- avoids extra midnight
AND (p.s = 0 OR p.s IS NULL) -- see note
AND (t.col IS NOT NULL OR t2.col IS NOT NULL) -- roundabout `OR`
ORDER BY p.idx DESC
LIMIT 0, 100;
注意:
为0
选择NULL
或s
;不允许两者兼而有之。这样,其中一个索引可用于提高效率(在简化AND (..OR..)
:
INDEX(s, timestamp)
INDEX(s, idx)
如果您保留s
的两个值,那么
INDEX(timestamp),
INDEX(idx)
虽然它们可能有用也可能没用。
Temp
和Temp2
需要
INDEX(col) -- unless `col` is already the `PRIMARY KEY`
如果您需要进一步讨论,请提供SHOW CREATE TABLE pp
。查看引擎,数据类型,当前索引等可能很方便。
IN( SELECT ... )
表现不佳
OR
优化得很差。 (我试图缓解这种情况。)
答案 1 :(得分:0)
编辑:我给出了以下答案,因为我误读了OP的查询。他在问
`from` in (select col from Temp)
or
`to` in (select col from Temp2)
我的回答好像他在问
`from` in (select col from Temp)
and
`to` in (select col from Temp2)
因此,下面带有两个连接的代码实际上代表AND
条件。我保持原样,但作为JOIN
如何用于表示AND
的示例,而Rick James在其答案中更正的代码显示了如何表示OR
。
如果col
和Temp
中的Temp2
都是唯一的,那么您的查询应该是
SELECT p.*
FROM pp p
JOIN Temp t
ON (p.from = t.col)
JOIN Temp2 t2
ON (p.to = t2.col)
WHERE p.timestamp >= '2016-01-01'
AND p.timestamp <= '2017-01-03'
AND (p.s = 0 OR p.s IS NULL)
ORDER BY p.idx DESC LIMIT 0, 100;
(如果col
不唯一,请将JOIN Temp t
替换为JOIN (SELECT DISTINCT col FROM Temp) t
,将Temp2
替换为{。}}。
当然,这也会在索引扫描之前对from
和to
执行检查,但至少它不会为每一行执行两个子查询......