Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Como melhorar o desempenho da consulta na pesquisa de administração do Django em campos relacionados (MySQL)


Depois de muitas investigações, descobri que o problema vem de como a consulta de pesquisa é criada para o campo de pesquisa do administrador (no ChangeList classe). Em uma pesquisa de vários termos (palavras separadas por espaço) cada termo é adicionado ao QuerySet encadeando um novo filter() . Quando há um ou mais campos relacionados nos search_fields , a consulta SQL criada terá muitos JOIN encadeados um após o outro com muitos JOIN para cada campo relacionado (veja meu pergunta relacionada para alguns exemplos e mais informações). Esta cadeia de JOIN existe para que cada termo seja pesquisado apenas no subconjunto de dados do filtro pelo termo precedente E, mais importante, que um campo relacionado precise ter apenas um termo (vs precisar ter TODOS os termos) para fazer uma correspondência. Consulte Abrangendo relacionamentos de vários valores nos documentos do Django para mais informações sobre este assunto. Tenho certeza de que é o comportamento desejado na maioria das vezes para o campo de pesquisa do administrador.

A desvantagem desta consulta (com campos relacionados envolvidos) é que a variação de desempenho (tempo para realizar a consulta) pode ser muito grande. Depende de muitos fatores:número de termos pesquisados, termos pesquisados, tipo de pesquisa de campo (VARCHAR, etc.), número de pesquisa de campo, dados nas tabelas, tamanho das tabelas, etc. Com a combinação certa é fácil ter uma consulta que levará quase uma eternidade (uma consulta que levará mais de 10 minutos para mim é uma consulta que levará uma eternidade no contexto desse campo de pesquisa).

A razão pela qual pode demorar tanto é que o banco de dados precisa criar uma tabela temporária para cada termo e escaneá-la principalmente para procurar o próximo termo. Então, isso se soma muito rapidamente.

Uma possível mudança para melhorar o desempenho é andar todos os termos no mesmo filter() . Desta forma, será apenas um JOIN por campo relacionado (ou 2 se for muitos para muitos) em vez de muitos mais. Essa consulta será muito mais rápida e com variação de desempenho muito pequena. A desvantagem é que os campos relacionados terão que ter TODOS os termos para corresponder, portanto, você pode obter menos correspondências em muitos casos.

ATUALIZAÇÃO


Conforme solicitado por trinchet aqui está o que é necessário para fazer a mudança de comportamento de pesquisa (para Django 1.7). Você precisa substituir o get_search_results() das classes de administração onde você deseja esse tipo de pesquisa. Você precisa copiar todo o código do método da classe base (ModelAdmin ) para sua própria classe. Então você precisa alterar essas linhas:
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))

Para isso:
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))

Este código não foi testado. Meu código original era para o Django 1.4 e acabei de adaptá-lo para o 1.7 aqui.