2010-12-02 123 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通過的表中提取行。