sql >> データベース >  >> RDS >> Mysql

このmysqlクエリで全表スキャンを回避するにはどうすればよいですか?

    EXPLAINに基づく 質問の出力では、クエリがすべきすべてのインデックスがすでにあります。 使用している、すなわち:

    CREATE INDEX idx_zip_from_distance
      ON zipcode_distances (zipcode_from, distance, zipcode_to);
    CREATE INDEX idx_zipcode ON venues (zipcode, id);
    CREATE INDEX idx_venue_id ON events (venue_id);
    

    (インデックス名から、idx_zip_from_distanceかどうかはわかりません 実際にはzipcode_toが含まれています 桁。そうでない場合は、それを追加して、カバーインデックスにする必要があります。 。また、venues.idを含めました idx_zipcodeの列 完全を期すためですが、これがテーブルの主キーであり、InnoDBを使用していると仮定すると、とにかく自動的に含まれます。)

    ただし、MySQLは別の、場合によっては最適ではないクエリプランを選択しているようです。このプランでは、すべてのイベントをスキャンし、会場と郵便番号を見つけてから、距離で結果をフィルタリングします。これはできた イベントテーブルのカーディナリティが十分に低い場合は、最適なクエリプランになりますが、この質問をしているという事実から、そうではないと思います。

    最適ではないクエリプランの理由の1つは、可能性があります 多すぎるという事実になります プランナーを混乱させるインデックス。たとえば、本当に 格納するデータがおそらく対称であるとすると、郵便番号テーブルにこれら3つのインデックスすべてが必要ですか?個人的には、上記のインデックスと、(zipcode_to, zipcode_from)の一意のインデックス(人工的なものがない場合は主キーにもなります)のみをお勧めします。 (できればこの順序で、zipcode_to=?で時折クエリを実行するようにします。 それを利用することができます。

    ただし、私が行ったいくつかのテストに基づくと、MySQLが間違ったクエリプランを選択している主な問題は、単にテーブルの相対的なカーディナリティにあると思われます。おそらく、実際のzipcode_distances テーブルは巨大です 、そしてMySQLは、WHEREの条件がどれだけあるかを理解するのに十分なほど賢くはありません。 条項は本当にそれを絞り込みます。

    その場合、最善かつ最も簡単な修正は、単にforceを実行することです。 MySQLが必要なインデックスを使用する

    select
        *
    from
        zipcode_distances z 
        FORCE INDEX (idx_zip_from_distance)
    inner join
        venues v    
        FORCE INDEX (idx_zipcode)
        on z.zipcode_to=v.zipcode
    inner join
        events e
        FORCE INDEX (idx_venue_id)
        on v.id=e.venue_id
    where
        z.zipcode_from='92108' and
        z.distance <= 5
    

    そのクエリを使用すると、実際に目的のクエリプランを取得する必要があります。 (FORCE INDEXが必要です ここでは、USE INDEXだけで クエリプランナーは、提案されたインデックスの代わりにテーブルスキャンを使用することを決定し、目的を達成できませんでした。これを最初にテストしたときに発生しました。)

    追伸これがSQLizeのデモで、両方とも およびなし FORCE INDEX 、問題を示しています。



    1. postgresqlがJSONをJSONBに移行する

    2. MicrosoftAccessからのSalesforceの一括挿入

    3. MySQLが昨日の日付を選択

    4. PDOで複数の行を挿入します