2010-12-02 84 views
5

考虑下面的查询:优化SQL查询,以避免全表扫描

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

交易表中的邮票列是TIMESTAMP和有它的索引。 如何更改此查询以避免全表扫描? (即使用邮票以外的天()函数)

谢谢!

+0

我不是要求“功能索引” - 它们不存在。 相反,我想转换这个查询以相同的方式,你可以将“SELECT * FROM table WHERE sqrt(column)= 2”转换为“SELECT * FROM table WHERE column = 4” – emx 2010-12-02 13:15:34

回答

7

这是我会怎么做:

添加一些额外的字段:年,月,日甚至是小时,分钟取决于你所期望的流量。 然后构建一个触发器来填充额外的字段,也许提前减去3小时的时间间隔。 最后在额外的字段上建立一些索引。

+0

谢谢,不是一个选项。 – emx 2010-12-02 12:06:44

+4

MySQL不支持函数索引 - Massimog的解决方案是指定每个可能的日期范围的唯一选择 - 但是由于您的查询可能会提取行的1/30,所以使用索引查找不会比使用索引查找快得多全表扫描。 – symcbean 2010-12-02 12:22:23

1

如果目标仅仅是避免全表扫描,你必须为交易主键(说叫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 
) 

该查询应不使用全表扫描(但是优化器可以决定使用全扫描,如果表格中的行数很少或者出于任何其他统计原因:))

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

0

分别计算你所需的邮票值运行您主查询之前,即

步骤1 - 计算期望印记值

第2步 - 运行查询,其中邮票>(计算值)

因为在步骤2中没有计算,您应该可以使用您的索引。

1

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

0

如果我理解正确,你基本上想要返回邮票落在每个月的第一个(减去3小时)的所有行?如果(这是一个很大的话),你有一个固定的窗口,比如最近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'; 

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

0

为了避免IN子句以及为MyISAM或InnoDB创建它,稍加修改petr的回答。

对于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通过的表中提取行。