Perché mysql ignora la chiave "ovvia" da utilizzare in questa semplice query di join?
-
03-07-2019 - |
Domanda
Ho quella che avrei pensato fosse una semplice domanda, ma ci vuole 'per sempre'. Non sono bravo con le ottimizzazioni SQL, quindi ho pensato di potertelo chiedere.
Ecco la query, con 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;
Risultato:
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, ''
Chiavi su posizione_studio:
Keyname Type Unique Packed Field Cardinality
PRIMARY BTREE Yes No id 65818
firms_location_name_en BTREE No No name_en 65818
Tasti su numero_firmphon:
Keyname Type Unique Packed Field Cardinality
PRIMARY BTREE Yes No id 85088
firms_firmphonenumber_firm_id BTREE No No location_id 85088
Mi sembra (per me) che mySQL si rifiuti di usare la chiave primaria della tabella83_location - ma non ho idea del perché.
Qualsiasi aiuto sarebbe molto apprezzato
Modifica dopo la pubblicazione della soluzione
Con l'ordine modificato di:
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;
Risultato:
"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,""
Perché ha deciso di usarli ora? mySQL fa delle strane scelte ... Qualsiasi approfondimento sarebbe di nuovo utile :)
Modifica con dettagli da Django
Inizialmente avevo questi modelli (abbreviati):
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")
La modifica del campo Meta.ordering della classe Locaion in (" name_en " ;,)
ha corretto la query in modo che non avesse un ordine spurio di.
Soluzione
Queste cose tendono ad essere per tentativi ed errori, ma prova a ordinare su companies_location.id piuttosto che companies_firmphonenumber.location_id. Hanno lo stesso valore, ma MySQL potrebbe quindi rilevare l'indice.
Altri suggerimenti
Lo sta usando, per il join; questo è il valore 'citiadmin.firms_location.id'
nella colonna ref
. Non appare in possible_keys
e key
perché non hai la clausola WHERE e riflette solo le chiavi che ha a disposizione per la clausola ORDER BY.
Se si desidera accelerare la query, provare a indicizzare name_en
.
Poiché non c'è dove, e poiché la cardinalità del campo di join è superiore a quella del campo di join, si sta calcolando che potrebbe anche ottenere tutto. L'uso dell'indice sul join non accelera, quindi ricorre alla minore ottimizzazione dell'utilizzo di un indice per l'ordinamento.
Per prima cosa, puoi fare USO per forzarlo a utilizzare l'indice specificato. Inoltre, prova a eseguire un'ottimizzazione per assicurarti che la cardinalità sia stimata correttamente. (Immagino che tu stia utilizzando INNO, che lo stima in una serie di "immersioni" casuali se questo è MyISAM, che in realtà lo sa, quindi mi chiedo perché la cardinalità assomigli a così.)
Non preoccuparti di indicizzare il nome o ecc. MySQL userà mai solo un indice per tabella per join, e l'indice lo farà semplicemente aumentare.
quanti dati? se solo poche righe, la maggior parte dei database eseguirà solo una scansione della tabella, indipendentemente dagli indici che hai