Pourquoi mysql ignore-t-il la clé "évidente" à utiliser dans cette requête de jointure simple?

StackOverflow https://stackoverflow.com/questions/804258

  •  03-07-2019
  •  | 
  •  

Question

J'ai ce que je pensais être une requête simple, mais cela prend "pour toujours". Je ne suis pas très doué avec les optimisations SQL, alors je pensais pouvoir vous poser la question.

Voici la requête avec 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;

Résultat:

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, ''

Clés sur les entreprises_location:

Keyname                 Type    Unique  Packed  Field   Cardinality
PRIMARY                    BTREE    Yes     No      id      65818
firms_location_name_en     BTREE    No      No      name_en 65818

Clés sur firm_firmphonenumber:

Keyname                     Type  Unique Packed  Field       Cardinality
PRIMARY                         BTREE Yes    No      id          85088
firms_firmphonenumber_firm_id   BTREE No     No      location_id 85088

Il me semble que MySQL refuse d'utiliser la clé primaire de la table firm_location, mais je ne sais pas pourquoi.

Toute aide serait beaucoup appréciée.

Modifier après la publication de la solution

Avec l'ordre modifié par:

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;

Résultat:

"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,""

Pourquoi at-il décidé de les utiliser maintenant? MySQL fait des choix étranges ... N'importe quelle idée aiderait encore:)

Modifier avec les détails de Django

À l'origine, j'avais ces modèles (abrégés):

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 modification du champ Meta.ordering de la classe Locaion en ("name_en", ) a corrigé la requête pour ne pas avoir l'ordre parasite par.

Était-ce utile?

La solution

Ces choses-là ont tendance à se faire par essais et erreurs, mais essayez plutôt de commander sur firm_location.id plutôt que sur fir_firmphonenumber.location_id. Ils ont la même valeur, mais MySQL peut alors récupérer l’index.

Autres conseils

Il l'utilise, pour la jointure; c'est la valeur 'citiadmin.firms_location.id' dans la colonne ref . Il n'apparaît pas dans possible_keys et dans key car vous n'avez pas de clause WHERE et il ne fait que refléter les clés dont il dispose pour la clause ORDER BY.

Si vous souhaitez accélérer votre requête, essayez d'indexer name_en .

Comme il n’ya pas d’endroit et que la cardinalité du champ de jointure est supérieure à celle du champ de jonction, il est calculé que l’on pourrait tout aussi bien tout obtenir. L’utilisation de l’index dans la jointure n’accélérera pas la tâche, c’est pourquoi il est préférable d’utiliser un index pour le tri.

Tout d’abord, vous pouvez utiliser USE pour le forcer à utiliser l’index que vous spécifiez. Essayez également d’optimiser pour vous assurer que la cardinalité est correctement estimée. (J'imagine que vous utilisez INNO, qui l’estimé par une série de "plongées" aléatoires; s’il s’agit de MyISAM, qui le sait, alors je me demande pourquoi la cardinalité a cet aspect.)

Ne vous préoccupez pas d'indexer le nom, etc. MySQL utilisera un seul index par table par jointure, et l'index ne fera que le gonfler.

combien de données? si seulement quelques lignes, la plupart des bases de données effectueront simplement une analyse de la table, quels que soient les index que vous avez

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top