完全なテーブルスキャンを避けるために、SQLクエリを最適化します

StackOverflow https://stackoverflow.com/questions/4334519

  •  30-09-2019
  •  | 
  •  

質問

次のクエリを検討してください。

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

スタンプ の列 トランザクション テーブルはタイムスタンプで、インデックスがあります。このクエリを変更するには、テーブルスキャンを避けるにはどうすればよいですか? (つまり、使用します スタンプ の外側 日() 働き)

ありがとう!

役に立ちましたか?

解決

これが私がそれをする方法です:

予想されるトラフィックに応じて、年、月、日、さらには時間、さらには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 ..., 、しかし、この場合はこれがインデックスを使用するかどうかはわかりません。 @Pettが示唆するように、月の日だったコラムを作成します。

メインクエリを実行する前に、目的のスタンプ値を個別に計算します。

ステップ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!タイムスタンプのミリ秒部分がどのように機能するかはわかりません。それに応じてパッドする必要があるかもしれません。

ペトルの答えを少し再加工して、句を避け、myisamまたはinnodbのためにそれを作るために。

myisamのために

ALTER TABLE Transactions ADD INDEX cover_1 (PK, Stamp)

または、すべてのインデックスにPKが暗黙的に含まれているInnodbの場合、

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が通過したテーブルから行を引き出すだけです。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top