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
、問題を示しています。