mysqlがこの単純な結合クエリで使用する「明らかな」キーを無視するのはなぜですか?
-
03-07-2019 - |
質問
単純なクエリだと思っていたものがありますが、「永遠に」かかります。私はSQLの最適化が得意ではないので、皆さんにお尋ねできると思いました。
EXPLAINを使用したクエリです:
EXPLAIN SELECT *
FROM `firms_firmphonenumber`
INNER JOIN `firms_location` ON (
`firms_firmphonenumber`.`location_id` = `firms_location`.`id`
)
ORDER BY
`firms_location`.`name_en` ASC,
`firms_firmphonenumber`.`location_id` ASC LIMIT 100;
結果:
id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra
1, 'SIMPLE', 'firms_location', 'ALL', 'PRIMARY', '', '', '', 73030, 'Using temporary; Using filesort'
1, 'SIMPLE', 'firms_firmphonenumber', 'ref', 'firms_firmphonenumber_firm_id', 'firms_firmphonenumber_firm_id', '4', 'citiadmin.firms_location.id', 1, ''
firms_locationのキー:
Keyname Type Unique Packed Field Cardinality
PRIMARY BTREE Yes No id 65818
firms_location_name_en BTREE No No name_en 65818
firms_firmphonenumberのキー:
Keyname Type Unique Packed Field Cardinality
PRIMARY BTREE Yes No id 85088
firms_firmphonenumber_firm_id BTREE No No location_id 85088
(私には)mySQLはfirms_locationテーブルの主キーの使用を拒否しているように見えますが、その理由はわかりません。
どのような助けでも大いにいただければ幸いです。
ソリューションの投稿後に編集
変更後の順序:
EXPLAIN SELECT *
FROM `firms_firmphonenumber`
INNER JOIN `firms_location` ON (
`firms_firmphonenumber`.`location_id` = `firms_location`.`id`
)
ORDER BY
`firms_location`.`name_en` ASC,
`firms_location`.id ASC LIMIT 100;
#`firms_firmphonenumber`.`location_id` ASC LIMIT 100;
結果:
"id","select_type","table","type","possible_keys","key","key_len","ref","rows","Extra"
1,"SIMPLE","firms_location","index","PRIMARY","firms_location_name_en","767","",100,""
1,"SIMPLE","firms_firmphonenumber","ref","firms_firmphonenumber_firm_id","firms_firmphonenumber_firm_id","4","citiadmin.firms_location.id",1,""
なぜこれらを使用することにしたのですか? mySQLは奇妙な選択をします...洞察は再び役立ちます:)
djangoの詳細で編集
元々、私はこれらの(短縮された)モデルを持っていました:
class Location(models.Model):
id = models.AutoField(primary_key=True)
name_en = models.CharField(max_length=255, db_index=True)
class Meta:
ordering = ("name_en", "id")
class FirmPhoneNumber(models.Model):
location = models.ForeignKey(Location, db_index=True)
number = PhoneNumberField(db_index=True)
class Meta:
ordering = ("location", "number")
LocaionのクラスのMeta.orderingフィールドを(" name_en&quot ;,)
に変更すると、クエリが誤った順序にならないようにクエリが修正されました。
解決
これらは試行錯誤による傾向がありますが、firms_firmphonenumber.location_idではなくfirms_location.idで注文してみてください。これらは同じ値ですが、MySQLはインデックスを取得する可能性があります。
他のヒント
結合に使用しています。これは、 ref
列の 'citiadmin.firms_location.id'
値です。 WHERE句がなく、ORDER BY句で使用可能なキーのみを反映しているため、 possible_keys
および key
には表示されません。
クエリを高速化する場合は、 name_en
のインデックスを作成してみてください。
場所がないため、結合フィールドのカーディナリティが結合フィールドのカーディナリティよりも高いため、すべてを取得できる可能性があると計算しています。結合でインデックスを使用しても速度は上がりません。そのため、ソートにインデックスを使用するよりも少ない最適化に頼っています。
まず、USEを実行して、指定したインデックスを強制的に使用できます。また、最適化を実行して、カーディナリティーが正しく推定されることを確認してください。 (あなたがINNOを使用していると推測しています。これは一連のランダムな「ダイビング」で推定します。これが実際に知っているMyISAMの場合、なぜカーディナリティがそれと同じように見えるのでしょうか?)
名前などのインデックス付けを行わないでください。MySQLは結合ごとにテーブルごとに1つのインデックスのみを使用し、インデックスはそれを一括します。
データ量は?数行のみの場合、ほとんどのデータベースは、インデックスが何であってもテーブルスキャンを実行します