Запросы с одним элементом в списке в __in очень медленные. В противном случае, супер быстро - PullRequest
0 голосов
/ 24 апреля 2020

Я получаю event_id's по имени с кодом ниже:

events = Events.objects.values_list('event__id', flat=True). \
    filter(name__in=names).distinct()

Все работает отлично, кроме случаев, когда names состоит только из одного имени. Если я изменю свой код на:

events = Events.objects.values_list('event__id', flat=True). \
    filter(name__in=names + ['x']).distinct()

Еще раз, он станет супер быстрым. Я серьезно схожу с ума, потому что это не имеет смысла. Я использовал print(events.query), и он в основном использует тот же запрос, только список меняется. Как это возможно?

Время выполнения с одним именем в списке длится 30-60 секунд, в противном случае это займет всего 100-1000 мс. Количество event_ids кардинально не меняется, поэтому проблема не в размере.

Я использовал EXPLAIN, и разница выглядит так:

        Extra: Using where; Using index
        Extra: Using index

И:

         type: range
         type: ref

Ответы [ 2 ]

0 голосов
/ 04 мая 2020

Более подробная информация и разъяснения определенно помогут.

Например:

  • Event модель (поможет воспроизвести проблему и даст необходимый фон)

  • events.query SQL утверждение (очень полезно)

  • values_list('event__id') предполагает, что Event модель может иметь ForeignKey для себя в сочетании с retrieving event_id's by name просто добавляет больше разочарования (это может быть действительно на самом деле)

  • сколько записей в таблице events? 100-1000ms не очень оптимальное время запроса

Первое, что можно предложить - взгляните на distinct().

Чтобы убедиться, что в выбранных столбцах присутствуют только выбранные столбцы и, следовательно, он отличается от одного столбца и более простого плана запроса - четкое упорядочение из QuerySet с empty order_by().

events = Events.objects.values_list('event__id', flat=True). \
    filter(name__in=names + ['x']).order_by().distinct()

Описание:

С distinct() Django выполняет SELECT DISTINCT sql запрос на удаление повторяющихся строк. Примечание duplicate rows, что означает уникальные строки во всех столбцах SELECT, а не с уникальными значениями в одном указанном c столбце.

values_list('event__id', flat=True) на первый взгляд может указывать, что только event_id присутствует в SELECT (то есть SELECT DISTINCT event_id FROM events ...), но это не так - Django просто принимает значения из столбцов, перечисленных в values_list результата, но SELECT может содержать любые другие столбцы Django считает, что требуется для запроса .

Таким образом, ваш events.query может фактически выглядеть как SELECT DISTINCT event_id, col_2, name FROM events ..., который не только дает различные результаты чем отличается для одного столбца (в некоторых случаях те же результаты, если включен уникальный столбец, например, id), но также может привести к более сложному плану запроса . Кроме того, col_2 может даже не присутствовать в QuerySet.

Django включает столбцы, которые, по его мнению, необходимы для запуска QuerySet. Т.е. это может быть столбец заказа по умолчанию, установленный на модели - тот, который присутствует, если в QuerySet порядок не установлен.

0 голосов
/ 30 апреля 2020

Вы проверяли type из names, когда просто один имя? Он должен работать одинаково независимо от длины списка имен, tuple и т. Д. c ... Однако, если в names указано только одно имя, тогда это строка, а не список .

Посмотрите пример в документации , если вы передаете строку, Django и python в целом обрабатывают строку как список символов.

Затем, если names='Django Reinhardt':

filter(name__in=names)

станет:

filter(name__in=['D', 'j', 'a', 'n', 'g', 'o', ' ',
                 'R', 'e', 'i', 'n', 'h', 'a', 'r', 'd', 't'])

, что, безусловно, не является желаемым поведением в вашем случае. Убедитесь, что names является списком, даже если указан только один. Поэтому, когда names=['Django Reinhardt]

Ваш код будет оцениваться в:

filter(name__in=['Django Reinhardt']

Если вы предоставите более подробную информацию о том, как вы получаете / создаете «имена», я мог бы предоставить больше помощи по этому вопросу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...