Моей грубая модель:Казалось бы, быстрый фильтр поле поиска медленно
class m_Interaction(models.Model):
fk_ip = models.ForeignKey('m_IP', on_delete=models.SET_NULL, null=True, related_name="interactions")
fk_query = models.ForeignKey('m_Query', on_delete=models.SET_NULL, null=True, related_name="interactions")
Баз данные используются: SQLite
Если я выполнить этот запрос набора
m_Interaction.objects.filter(fk_query=None).filter(fk_ip__in=user.ips.all()).select_related('fk_query')
это занимает 5 секунд.
Если удалить filter(fk_query=None)
заявление, остающийся запрос набора
m_Interaction.objects.filter(fk_ip__in=user.ips.all()).select_related('fk_query')
выполняет всего 100 миллисекунд.
Не должно быть filter(fk_ip__in=user.ips.all())
намного дороже? Или, по крайней мере, почему заявление filter(fk_query=None)
так медленно? Это должно быть простое «сравнение с Null» -lookup.
SQL-запросов с filter(fk_query=None)
:
SELECT "data_manager_m_interaction"."id",
"data_manager_m_interaction"."fk_ip_id",
"data_manager_m_interaction"."fk_query_id",
"data_manager_m_query"."id",
"data_manager_m_query"."fk_ip_id"
FROM "data_manager_m_interaction"
LEFT OUTER JOIN "data_manager_m_query"
ON ("data_manager_m_interaction"."fk_query_id" = "data_manager_m_query"."id")
WHERE ("data_manager_m_interaction"."fk_ip_id" IN (SELECT U0."id" FROM "data_manager_m_ip" U0 WHERE U0."fk_user_id" = 1339)
AND "data_manager_m_interaction"."fk_query_id" IS NULL)
ORDER BY "data_manager_m_interaction"."timestamp" ASC
LIMIT 1
SQL-запросов без filter(fk_query=None)
:
SELECT "data_manager_m_interaction"."id",
"data_manager_m_interaction"."fk_ip_id",
"data_manager_m_interaction"."fk_query_id",
"data_manager_m_query"."id",
"data_manager_m_query"."fk_ip_id"
FROM "data_manager_m_interaction"
LEFT OUTER JOIN "data_manager_m_query"
ON ("data_manager_m_interaction"."fk_query_id" = "data_manager_m_query"."id")
WHERE "data_manager_m_interaction"."fk_ip_id" IN (SELECT U0."id" FROM "data_manager_m_ip" U0 WHERE U0."fk_user_id" = 1339)
ORDER BY "data_manager_m_interaction"."timestamp" ASC
LIMIT 1
EXPLAIN план запроса (с фильтром):
[(0, 0, 0, 'SEARCH TABLE data_manager_m_interaction USING INDEX data_manager_m_interaction_c50f4040 (fk_query_id=?)'),
(0, 0, 0, 'EXECUTE LIST SUBQUERY 1'),
(1, 0, 0, 'SEARCH TABLE data_manager_m_ip AS U0 USING COVERING INDEX data_manager_m_ip_f569ccde (fk_user_id=?)'),
(0, 1, 1, 'SEARCH TABLE data_manager_m_query USING INTEGER PRIMARY KEY (rowid=?)'),
(0, 0, 0, 'USE TEMP B-TREE FOR ORDER BY')]
EXPLAIN QUERY ПЛАН (без фильтра)
[(0, 0, 0, 'SEARCH TABLE data_manager_m_interaction USING INDEX data_manager_m_interaction_c669518a (fk_ip_id=?)'),
(0, 0, 0, 'EXECUTE LIST SUBQUERY 1'),
(1, 0, 0, 'SEARCH TABLE data_manager_m_ip AS U0 USING COVERING INDEX data_manager_m_ip_f569ccde (fk_user_id=?)'),
(0, 1, 1, 'SEARCH TABLE data_manager_m_query USING INTEGER PRIMARY KEY (rowid=?)'),
(0, 0, 0, 'USE TEMP B-TREE FOR ORDER BY')]
Во-первых, вы можете попробовать 'filter (fk_query__isnull = True)' вместо 'fk_query = None' и посмотреть, улучшает ли это что-либо, но я бы выполнил' print your-query.query', чтобы увидеть исходную инструкцию sql и посмотреть разница. –
Согласен, что это кажется нелогичным. Не имеет значения, измените ли вы порядок фильтров или объедините их в один фильтр? 'filter (fk_query = None, fk_ip__in = user.ips.all())' –
вы используете mysql? я подозреваю, что вы. Если это так, то на самом деле это не удивительно. – e4c5