优化SQL查询以避免全表扫描

时间:2010-12-02 11:36:14

标签: sql mysql compound-index

考虑以下问题:

SELECT * FROM Transactions
WHERE day(Stamp - interval 3 hour) = 1;

Transactions 表中的标记列是TIMESTAMP,并且有一个索引。 我怎样才能更改此查询以避免全表扫描? (即,在 day()函数之外使用 Stamp

谢谢!

6 个答案:

答案 0 :(得分:8)

我就是这样做的:

添加一些额外的字段:YEAR,MONTH,DAY甚至HOUR,MINUTE,具体取决于您期望的流量。 然后构建一个触发器来填充额外的字段,可以提前减去3小时的间隔。 最后在额外的字段上建立一些索引。

答案 1 :(得分:1)

如果目标只是为了避免全表扫描并且您有一个PRIMARY KEY(比如名为PK),请考虑添加覆盖索引

ALTER TABLE Transactions ADD INDEX cover_1 (PK, Stamp)

然后

SELECT * FROM Transactions WHERE PK IN (SELECT PK FROM Transactions
WHERE day(Stamp - interval 3 hour) = 1
 )

此查询不应使用全表扫描(但是,如果表中的行数很小或者出于其他任何统计原因,优化程序可能决定使用完全扫描:))

更好的方法是使用临时表而不是子查询。

答案 2 :(得分:1)

你经常可以重写这个函数,所以你有一些看起来像WHERE Stamp=XXXX的东西,而XXXX就是一些表达式。您可以为每个月WHERE Stamp BETWEEN timestamp('2010-01-01 00:00:00') AND timestamp ('2010-01-01 23:59:59') OR Stamp BETWEEN ...创建一系列BETWEEN语句,但我不确定在这种情况下会使用索引。我建立了一个专栏,就像@petr建议的那样。

答案 3 :(得分:0)

在运行主查询之前单独计算所需的Stamp值,即

步骤1 - 计算所需的印记值

步骤2 - 运行查询,其中Stamp> (计算值)

由于步骤2中没有计算,您应该可以使用索引。

答案 4 :(得分:0)

如果我理解正确的话,你基本上想要返回每个月第一张邮票的所有行(减去3小时)?如果(这是一个很大的if),你有一个固定的窗口,比如说最近的6个月,你可以枚举6个范围测试。但是,我仍然不确定索引访问会更快。

select *
  from transactions
 where stamp between timestamp '2010-06-01 03:00:00' and timestamp '2010-06-02 02:59:59'
    or stamp between timestamp '2010-07-01 03:00:00' and timestamp '2010-07-02 02:59:59'
    or stamp between timestamp '2010-08-01 03:00:00' and timestamp '2010-08-02 02:59:59'
    or stamp between timestamp '2010-09-01 03:00:00' and timestamp '2010-09-02 02:59:59'
    or stamp between timestamp '2010-10-01 03:00:00' and timestamp '2010-10-02 02:59:59'
    or stamp between timestamp '2010-11-01 03:00:00' and timestamp '2010-11-02 02:59:59'
    or stamp between timestamp '2010-12-01 03:00:00' and timestamp '2010-12-02 02:59:59';

NB!我不确定时间戳的毫秒部分是如何工作的。您可能需要相应地填充它。

答案 5 :(得分:0)

重新处理petr的答案以避免使用IN子句,并将其用于MyISAM或InnoDB。

对于MyISAM

ALTER TABLE Transactions ADD INDEX cover_1 (PK, Stamp)

或者,对于InnoDB,PK隐含地包含在每个索引中,

ALTER TABLE Transactions ADD INDEX Stamp (Stamp)

然后

SELECT * 
FROM Transactions LEFT JOIN
  (
  SELECT PK 
  FROM Transactions 
  WHERE DAYOFMONTH(Stamp - interval 3 hour) = 1
  ) a ON Transactions.PK=a.PK

子查询将仅执行索引,外部查询将仅从a.PK所在的表中提取行。