0

私はINNODBテーブルを持っていますlevels:

+--------------------+------+------+-----+ ------+-------+
| | フィールド | フィールド タイプ | ヌル | キー | キー | デフォルト | エクストラ |
+--------------------+------+------+-----+ ------+-------+
| | ID | int(9) | いいえ | PRI | ヌル | | |
| | レベル名 | varchar(20) | いいえ | | | ヌル | | |
| | ユーザー ID | int(10) | いいえ | | | ヌル | | |
| | ユーザー名 | varchar(45) | いいえ | | | ヌル | | |
| | 評価 | 10 進数 (5,4) | いいえ | | | 0.0000 | | |
| | 投票 | int(5) | いいえ | | | 0 | | |
| | 再生 | int(5) | いいえ | | | 0 | | |
| | date_published | 日付 | いいえ | マル | ヌル | | |
| | ユーザーコメント | varchar(255) | いいえ | | | ヌル | | |
| | playable_character | int(2) | いいえ | | | 1 | | |
| | 特集されています | tinyint(1) | いいえ | マル | 0 | | |
+--------------------+------+------+-----+ ------+-------+

約 400 万行あります。フロントエンド機能のため、さまざまなフィルターと並べ替えを使用してこのテーブルをクエリする必要があります。それらは、、、、playable_characterおよびにありratingます。過去の日、週、月、またはいつでも (過去 3 年間) 表示するようにフィルター処理できます。ページングもあります。したがって、ユーザーの選択に応じて、クエリは次のいずれかのようになります。playsdate_publisheddate_published

SELECT * FROM levels
WHERE playable_character = 0 AND
    date_published BETWEEN date_sub(now(), INTERVAL 3 YEAR) AND now()
ORDER BY date_published DESC
LIMIT 0, 1000;

SELECT * FROM levels
WHERE playable_character = 4 AND
    date_published BETWEEN date_sub(now(), INTERVAL 1 WEEK) AND now()
ORDER BY rating DESC
LIMIT 4000, 1000;

SELECT * FROM levels
WHERE playable_character = 5 AND
    date_published BETWEEN date_sub(now(), INTERVAL 1 MONTH) AND now()
ORDER BY plays DESC
LIMIT 1000, 1000;

ここでの最初の例のクエリでidx_date_char(date_published, playable_character)うまく機能するインデックスから始めましたdate_published基本的には. EXPLAIN を使用すると、'using index condition' が得られます。これは良いことです。同じ 2 つのインデックス付きの列が WHERE 句と ORDER BY 句に存在するため、インデックスが機能する理由を理解していると思います。

plays私の問題は、またはで注文するクエリにありratingます。3 番目の列を紹介していることは理解していますが、私の人生では、考えられるほぼすべてのバリエーションを試しても、うまく機能するインデックスを取得することはできません。すぐ。たぶん、クエリは別の方法で記述できますか?

私はそれを追加する必要がratingありplays、常に として照会されDESCます。または のいずれかのみdate_publishedが可能です。DESCASC

どんな提案でも大歓迎です。ティア。

4

3 に答える 3

1

where 句 AND order by で使用される列は、インデックスの一部である必要があります。私は上のインデックスを持っているだろう

( playable_character, date_published DESC, rating DESC, plays DESC )

私がプレイアブル キャラクターを最初に置く理由は、その ID をプライマリにしたいからです。評価と再生は、ORDER BY 句を支援するための乗り物にちょうど沿っています)。

インデックスはこのように考えてください。Date_Published、次に Playable_Character で注文した場合、箱の部屋を考えてみてください。各ボックスには日付があります。特定の日付のそのボックス内には、文字順に表示されます。つまり、3 年分のデータがあり、過去 3 年間のすべてのボックスを開いて、探しているキャラクターを見つける必要があります。

では、こう考えてみてください。各ボックスはキャラクターごとで、その中ですべての日付が事前に並べ替えられています。1 つのボックスに移動して、それを開きます... 問題の日付に移動し、必要な XY 範囲からレコードを取得します。これで、これらのレコードの単純な順序を適用できます。

于 2013-10-15T16:09:13.093 に答える
1

クエリに のような範囲述語が含まれる場合、BETWEENインデックス内の列の順序が重要になります。

  • 最初に、等値述語によって参照される列を 1 つ以上含めます。
  • 次に、範囲述語によって参照される 1 つの列を含めます。
  • 範囲述語によって参照される列の後の索引内の列は、他の範囲述語またはソートには使用できません。
  • 範囲述語がない場合は、並べ替え用の列を追加できます。

したがって、最初のクエリは のインデックスの恩恵を受けることができます(playable_character, date_published)。オプティマイザはインデックス順に行をフェッチするだけなので、ソートはノーオペレーションである必要があります。

2 番目と 3 番目のクエリは、範囲述語があり、別の列で並べ替えているため、ファイル並べ替えを実行するようにバインドされています。等式述語しかない場合は、3 番目の列を使用してファイルソートを回避できますが、範囲述語がある場合は機能しません。

期待できる最善の方法は、条件によって結果セットのサイズが縮小され、あまり多くのソート マージ パスを実行せずにメモリ内でソートできるようになることです。sort_buffer_sizeを増やすことでこれを助けることができますが、スレッドごとに割り当てられるため、増やしすぎないように注意してください。

インデックス定義のASC/キーワードは、MySQL では違いがありません。http://dev.mysql.com/doc/refman/5.6/en/create-index.html を参照してください:DESC

これらのキーワードは、昇順または降順のインデックス値ストレージを指定するための将来の拡張で許可されます。現在、それらは解析されますが無視されます。インデックス値は常に昇順で格納されます。

于 2013-10-15T20:30:43.597 に答える
1

各クエリに対して、この方法で並べ替えられたデータをうまく利用できるようです。

  1. playable_character, date_published
  2. playable_character, date_published, rating
  3. playable_character, date_published, プレイ

最初のクエリでソートする必要があるデータは、たまたま 2 番目と 3 番目のクエリで必要なデータのサブセットであるため、それを取り除くことができることに注意してください。

DESCまた、インデックスにorを追加することASCは構文的には正しいですが、その機能は現在サポートされていないため、実際には何も変更されないことに注意してください (将来サポートされることが期待されているため、そこに存在します)。すべてのインデックスは昇順で格納されます。詳細はこちら

したがって、これらは作成する必要があるインデックスです。

ALTER TABLE levels ADD INDEX (playable_character, date_published, rating)
ALTER TABLE levels ADD INDEX (playable_character, date_published, plays)

これにより、そこにある 3 つのクエリが Forrest Gump よりも高速に実行されるはずです。

于 2013-10-15T16:20:13.567 に答える