如何使sql查询使用索引范围扫描

时间:2017-01-03 08:59:52

标签: mysql query-optimization

我有一个关于mysql如何优化查询的问题。

Mark as JavaScript

Mysql在时间戳上使用全索引扫描,附带两个子查询。

我不明白为什么它首先不在时间戳上使用索引范围扫描,然后再应用子查询。

这有什么特别的原因吗? 如果没有,我可以强制它使用索引范围扫描吗?

2 个答案:

答案 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选择NULLs;不允许两者兼而有之。这样,其中一个索引可用于提高效率(在简化AND (..OR..)

之后)
INDEX(s, timestamp)
INDEX(s, idx)

如果您保留s的两个值,那么

INDEX(timestamp),
INDEX(idx)

虽然它们可能有用也可能没用。

TempTemp2需要

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

如果colTemp中的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替换为{。}}。

当然,这也会在索引扫描之前对fromto执行检查,但至少它不会为每一行执行两个子查询......