Opened 7 years ago
Closed 7 years ago
#28211 closed Cleanup/optimization (fixed)
Inefficient query produced when using OR'ed Q objects
Reported by: | Tom Forbes | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Ready for checkin | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
When using OR'ed Q objects in a .filter()
call Django could be more intelligent about the query it produces.
For example, the following two queries both produce LEFT OUTER JOIN
:
SomeModel.objects.filter(Q(related_objects__field=1) | Q(related_objects__other_field=1)
SomeModel.objects.filter(Q(related_objects__field=1) | Q())
In the case of the second query it could be reduced to a INNER JOIN
as the OR
is redundant and it is functionally the same as a .filter(related_objects__field=1)
.
It is also quite a common pattern to do:
filters = Q() if condition: filters |= Q(x=1) if other_condition: filters |= Q(y=2)
And with the current implementation it will always produce a query that assumes filters
is a valid OR
. Django should/could be more intelligent and detect if there is only one OR
condition, and reduce it.
Change History (6)
comment:1 by , 7 years ago
Description: | modified (diff) |
---|
comment:2 by , 7 years ago
Triage Stage: | Unreviewed → Accepted |
---|
comment:3 by , 7 years ago
Has patch: | set |
---|
I added a patch here: https://github.com/django/django/pull/8517
I took the easy route and fixed what I think is an issue in the Q
object. Combining empty Q
objects should be a no-op. This effectively fixes this ticket without many changes.
comment:4 by , 7 years ago
Triage Stage: | Accepted → Ready for checkin |
---|---|
Version: | 1.10 → master |
I think the provided patch solves this in an efficient way.
We could add folding logic in the query compiler as well but given it will be very hard to generate combined Q
with a falsey sides after with this patch (one would have to call Q.add
manually) I don't think it's required.
Tentatively accepting, though I don't know if this is feasible or correct. If a patch is provided, hopefully the test suite will validate its correctness.