2017-01-03 64 views
0

我有一个关于如何MySQL优化查询的问题。如何使SQL查询使用索引范围扫描

select * 
from pp 
where 1 
    and timestamp >= '2016-01-01' and timestamp <= '2017-01-03' 
    and (s = 0 or s is NULL) 
    and (
     from in (select col from Temp) 
     or 
     to in (select col from Temp2) 
    ) 
order by idx desc limit 0, 100; 

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

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

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

回答

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; 

注: 选择0NULL代表s;不要同时允许。这样一来,这些指标之一,可用于额外的效率(您简化后的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优化不佳。 (我试图缓解这一点。)

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在两个TempTemp2独特,您的查询应该像

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 (SELECT DISTINCT col FROM Temp) t,同样与Temp2取代JOIN Temp t)。

当然,这执行指数之前fromto支票扫描为好,但至少它不会为每个行执行两个子查询......

+0

我不认为这个公式是正确的--OP在两次查找之间有'OR';你有效地说'AND'。 –

+0

你是对的。我误解了OP的查询。我编辑了我的答案。 – Dario