考虑以下查询:
SELECT * FROM Transactions
WHERE day(Stamp - interval 3 hour) = 1;
Transactions表中的Stamp列是一个 TIMESTAMP,并且上面有一个索引。如何更改此查询以避免全表扫描?(即在day()函数之外使用Stamp )
谢谢!
考虑以下查询:
SELECT * FROM Transactions
WHERE day(Stamp - interval 3 hour) = 1;
Transactions表中的Stamp列是一个 TIMESTAMP,并且上面有一个索引。如何更改此查询以避免全表扫描?(即在day()函数之外使用Stamp )
谢谢!
我会这样做:
添加一些额外的字段:YEAR、MONTH、DAY 甚至 HOUR、MINUTE,具体取决于您期望的流量。然后构建一个触发器来填充额外的字段,可能会提前减去 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 的东西。您可以为每个月创建一系列 BETWEEN 语句WHERE Stamp BETWEEN timestamp('2010-01-01 00:00:00') AND timestamp ('2010-01-01 23:59:59') OR Stamp BETWEEN ...
,但我不确定在这种情况下是否会使用索引。正如@petr 所建议的那样,我将建立一个列是一个月中的某一天。
在运行主查询之前分别计算所需的 Stamp 值,即
第 1 步 - 计算所需的 Stamp 值
第 2 步 - 运行查询,其中 Stamp >(计算值)
因为第 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';
注意!我不确定时间戳的毫秒部分是如何工作的。您可能需要相应地填充它。
修改 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 通过的表中提取行。