题
考虑以下查询:
SELECT * FROM Transactions
WHERE day(Stamp - interval 3 hour) = 1;
这 邮票 列 交易 表是时间戳,上面有索引。我该如何更改此查询以避免全表扫描? (也就是说,使用 邮票 外面 天() 功能)
谢谢!
解决方案
这就是我这样做的方式:
添加一些额外的字段:根据您期望的流量,一年,月,每天甚至小时,分钟。然后构建一个触发器以填充额外的字段,也许会提前减去3小时的间隔。最后在额外的字段上构建一些索引。
其他提示
如果目标只是为了避免全表扫描,并且您有一个主键(例如,命名为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
)
此查询不应使用完整的表扫描(但是,如果表格中的行很小或出于任何其他统计原因,那么优化器可能会决定使用完整的扫描:))
更好的方法可能是使用临时表而不是子查询。
您通常可以重写该功能,以便您拥有看起来像 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 ...
, ,但我不确定在这种情况下,这会使用索引。正如@Petr所建议的那样,我会构建一个每月的专栏。
在运行主查询之前,分别计算您所需的邮票值,即
步骤1-计算所需的邮票值
步骤2-在邮票>(计算值)的地方运行查询
由于步骤2中没有计算,因此您应该能够使用索引。
如果我正确理解它,您基本上想返回邮票每月第一个邮票(减去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';
NB!我不确定时间戳的毫秒如何工作。您可能需要相应地垫。
重新加工PETR的答案,以避免在子句中,并为Myisam或Innodb做出。
对于迈萨姆
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通过的表中拉出行。