Mysql
 sql >> Base de données >  >> RDS >> Mysql

Comment améliorer les performances des requêtes dans la recherche d'administration Django sur des champs connexes (MySQL)

Après de nombreuses recherches, j'ai découvert que le problème venait de la façon dont la requête de recherche est construite pour le champ de recherche de l'administrateur (dans la ChangeList classer). Dans une recherche multi-termes (mots séparés par un espace) chaque terme est ajouté au QuerySet en enchaînant un nouveau filter() . Lorsqu'il y a un ou plusieurs champs associés dans search_fields , la requête SQL créée aura beaucoup de JOIN enchaînés les uns après les autres avec de nombreux JOIN pour chaque champ connexe (voir mon question connexe pour quelques exemples et plus d'informations). Cette chaîne de JOIN est là pour que chaque terme soit recherché uniquement dans le sous-ensemble de filtre de données par le terme précédent ET, le plus important, qu'un champ connexe n'ait besoin d'avoir qu'un seul terme (vs besoin d'avoir TOUS les termes) pour faire une correspondance. Voir Spanning multi-valued relations dans la documentation de Django pour plus d'informations à ce sujet. Je suis presque sûr que c'est le comportement recherché la plupart du temps pour le champ de recherche de l'administrateur.

L'inconvénient de cette requête (avec des champs associés impliqués) est que la variation des performances (temps d'exécution de la requête) peut être très importante. Cela dépend de nombreux facteurs :nombre de termes recherchés, termes recherchés, type de recherche de champ (VARCHAR, etc.), nombre de recherche de champ, données dans les tables, taille des tables, etc. Avec la bonne combinaison, c'est facile pour avoir une requête qui prendra une éternité (une requête qui prend plus de 10 minutes pour moi est une requête qui prend une éternité dans le contexte de ce champ de recherche).

La raison pour laquelle cela peut prendre autant de temps est que la base de données doit créer une table temporaire pour chaque terme et l'analyser entièrement pour rechercher le terme suivant. Donc, cela s'additionne très rapidement.

Un changement possible à faire pour améliorer les performances est de ANDed tous les termes dans le même filter() . De cette façon, il n'y aura qu'un seul JOIN par champ connexe (ou 2 s'il s'agit d'un plusieurs à plusieurs) au lieu de beaucoup plus. Cette requête sera beaucoup plus rapide et avec de très petites variations de performances. L'inconvénient est que les champs associés devront avoir TOUS les termes à faire correspondre, vous pouvez donc obtenir moins de correspondances dans de nombreux cas.

MISE À JOUR

Comme demandé par trinchet voici ce qui est nécessaire pour faire le changement de comportement de recherche (pour Django 1.7). Vous devez remplacer le get_search_results() des classes d'administration où vous voulez ce type de recherche. Vous devez copier tout le code de la méthode de la classe de base (ModelAdmin ) à votre propre classe. Ensuite, vous devez modifier ces lignes :

for bit in search_term.split():
    or_queries = [models.Q(**{orm_lookup: bit})
                  for orm_lookup in orm_lookups]
    queryset = queryset.filter(reduce(operator.or_, or_queries))

À cela :

and_queries = []
for bit in search_term.split():
    or_queries = [models.Q(**{orm_lookup: bit})
                  for orm_lookup in orm_lookups]
    and_queries.append(Q(reduce(operator.or_, or_queries)))
queryset = queryset.filter(reduce(operator.and_, and_queries))

Ce code n'est pas testé. Mon code d'origine était pour Django 1.4 et je viens de l'adapter pour 1.7 ici.