2017-11-22 1 views
2

を説明:MySQLのインデックスと私はこのクエリ持って計画

SELECT * FROM dwDimDate d 
LEFT JOIN tickets t FORCE INDEX FOR JOIN (idx_tickets_id_and_date) ON 
DATE_FORMAT(t.ticket_date, '%Y%m%d') = d.date_key 
LEFT JOIN sales s ON s.ticket_id = t.ticket_id 
WHERE d.date_key BETWEEN 20130101 AND 20131231 
GROUP BY d.date_key 

を、私はそれを最適化する助けを探しています。私は説明プランを理解し、それに基づいて最適化することができるすべてを読んできましたが、MySQLがチケットテーブルでALLタイプのルックアップを使用するのを防ぐことはできません。

INDEXES:

enter image description here

EXPLAIN PLANを:

enter image description here

私はFORCE INDEX FORはのオフインデックスにそれを試してみて、取得するためにJOINを使用してみましたしかし、それはヒントを取っていないようです。

dwDimDateは、年の日数を持つ日付ディメンションです。このシナリオでは、365日に制限され、その日付範囲内のすべてのチケットを見つけるのが速くなると思います。その日付範囲内には約5Kのチケットしかないはずです。

ご協力いただければ幸いです。私は "ALL"ルックアップを削除するためにどの戦略を採用するかを理解する方法を知らない。私は将来このことをどうやって行うのかを理解したいので、もしあなたが「魚を教えてください」と助けてくれるなら、それは素晴らしいことです。

EDIT 現在、クエリの実行には11秒かかりますが、これは本番環境で問題となります。

+0

本当に「LEFT」が必要ですか? –

+0

これは不適切に形成されています - 指定された日付に対して複数のチケットが存在する場合、 'tickets'の値は配信されるべきですか? –

答えて

3
ON DATE_FORMAT(t.ticket_date, '%Y%m%d') = d.date_key 

このように、t.ticket_date列の関数を使用すると、これはインデックスを使用しません。

FORCE INDEXは、魅力的でない表現を魔法のような表現に魔法のようにしません。オプティマイザは、テーブルスキャンが無限に高価であると仮定することを示唆しています。したがって、オプティマイザは、この結合式でテーブルスキャンを実行する必要があるため、「まあ、それはあなたを吸う」と言うでしょう。

解決策の1つは、t.ticket_dateとd.date_keyを共通の形式で格納することです。両方にDATE列または 'YYYYmmdd'文字列を使用します。

第2の可能な解決策:t.ticket_dateに基づいて仮想列を作成し、仮想列を索引付けします。

ALTER TABLE tickets 
    ADD COLUMN ticket_date_yyyymmdd AS (DATE_FORMAT(ticket_date, '%Y%m%d'), 
    ADD INDEX (ticket_date_yyyymmdd); 
+0

完璧、ありがとう!それは後見では明らかなようですが、私は何かを学びました。私は日付ディメンションテーブルを変更し、チケットテーブルの形式で列を追加しました。今は15秒半です! – hyphen

2

問題は、列自体の値ではなく、列の関数に参加しようとしていることです。したがって、ticket_dateでインデックスを使用して結合を実行することはできません。

理想的には、ticket_dateがdate_keyと互換性のある形式になっていることを確認してください。単純な比較または範囲クエリを実行するだけです。これがあなたのためのオプションではなく、InnoDBでMySQL(5.7.8+)の比較的新しいバージョンを使用している場合は、仮想カラムを作成してeffectively create a functional indexを作成することができます。

0

これは有効なクエリに近い可能性があり、少なくともMySQL 5ではいくらか高速化する必要があります。6以上:

SELECT * 
    FROM dwDimDate AS d 
    LEFT JOIN 
     (SELECT MIN(ticket_id) AS one_tic_id, 
        COUNT(*) AS num_tickets, 
        DATE(ticket_date) AS date_key 
      FROM tickets t 
      LEFT JOIN sales s 
       ON s.ticket_id = t.ticket_id 
     ) AS ts USING (date_key) 
    WHERE d.date_key >= '2013-01-01' 
     AND d.date_key < '2013-01-01' + INTERVAL 1 MONTH 
    GROUP BY d.date_key; 
関連する問題