Почему Django QuerySet создает запрос с двумя внутренними объединениями с предложениями AND вместо этого или с одним внутренним объединением с OR при объединении фильтров? - PullRequest
0 голосов
/ 08 мая 2020

Добрый вечер,

Я следую django документации по созданию запросов, и появился этот пример:

Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)

Я надеялся, что соответствующий запрос SQL при этом задействовано только одно внутреннее соединение и предложение OR, поскольку соответствующие результаты являются записями, которые удовлетворяют одному или обоим условиям.

Тем не менее, проверка запроса набора запросов вернула следующее:

>>> qs = Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)
>>> print(qs.query)
SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline"
FROM "blog_blog" 
INNER JOIN "blog_entry" ON ("blog_blog"."id" = "blog_entry"."blog_id") 
INNER JOIN "blog_entry" T3 ON ("blog_blog"."id" = T3."blog_id") 
WHERE ("blog_entry"."headline" LIKE %Lennon% ESCAPE '\' 
AND T3."pub_date" BETWEEN 2008-01-01 AND 2008-12-31)

Вы можете видеть, что он выполняет ДВА внутренних соединения.

Разве результат не будет таким же, как:

SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."tagline" 
FROM "blog_blog" 
INNER JOIN "blog_entry" ON ("blog_blog"."id" = "blog_entry"."blog_id")
WHERE ("blog_entry"."headline" LIKE %Lennon% ESCAPE '\' OR "blog_entry"."pub_date" BETWEEN 2008-01-01 AND 2008-12-31)

?

И этот более поздний запрос AFAIK быстрее.

(добавлены разрывы строк для удобства чтения)

Ответы [ 2 ]

1 голос
/ 08 мая 2020

Что ж, после некоторого исследования я узнал несколько вещей.

Во-первых, из этого ответа , способ реализации запроса ИЛИ:

Blog.objects.filter(Q(entry__headline__contains='Lennon') | Q(entry__pub_date__year=2008))

И из этой возни:

https://www.db-fiddle.com/f/f8SGzTLeyr7DNZUaCx9HVL/0

Я понял, что два запроса в OP не дают одинаковых результатов: во 2 внутренних объединяется декартово произведение результатов (2 «Lennon» * 2 «2008»), а во втором - 3 результата (и это действительно быстрее).

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

Полагаю, это потому, что вы дважды выполняете filter ().

Попробуйте

qs = Blog.objects.filter(entry__headline__contains='Hello', entry__pub_date__year=2008)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...