#18414 closed Bug (fixed)
queryset.exists() returns False when queryset is distinct and sliced
Reported by: | bitrut | Owned by: | err |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 1.4 |
Severity: | Normal | Keywords: | orm |
Cc: | paluho@…, bitrut@… | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | yes |
Easy pickings: | no | UI/UX: | no |
Description
Consider this:
User.objects.count() 3
This queryset
User.objects.all()[1:].exists() True
produces SQL
SELECT (1) AS "a" FROM "user" LIMIT 1 OFFSET 1
so the result is correct.
But when you add distinct()
:
User.objects.distinct()[1:].exists() False
the SQL is
SELECT DISTINCT (1) AS "a" FROM "user" LIMIT 1 OFFSET 1
which returns nothing, so the exists()
returns False
which is incorrect.
DISTINCT
narrows results to just one 1 and OFFSET
omits the first result and goes to the next one which does not exist.
It's because DISTINCT
has the higher priority than OFFSET
in SQL.
I don't know the perfect solution. Maybe in the case of using distinct()
, slicing and exists()
together an exception should be thrown. Or the exists()
function should notice this case and call count()
, but this would lead to unexpected DB usage.
Change History (10)
comment:1 by , 12 years ago
Cc: | added |
---|
comment:2 by , 12 years ago
Triage Stage: | Unreviewed → Accepted |
---|
comment:3 by , 12 years ago
Cc: | added |
---|
comment:4 by , 12 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:5 by , 12 years ago
Has patch: | set |
---|
Here is my pull request: https://github.com/django/django/pull/161
comment:6 by , 12 years ago
The pull request doesn't seem to do what Luke asked - it disallows the usage in any filtered situation, not just in conjunction with .distinct(). It seems likely there are users relying on this currently.
As for fixing the distinct() + limit + .exists() - it should be done by not doing select distinct(1) but instead select distinct {normal select fields} instead. I wonder if this would be straightforward to do?
comment:7 by , 11 years ago
Patch needs improvement: | set |
---|
comment:8 by , 11 years ago
It seems this will be relatively easy to fix, just do
if not q.distinct: q.clear_select_clause()
instead of always doing clear_select_clause(). I'll test this & commit if things seem to work OK.
comment:9 by , 11 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
QuerySet.exists()
should throw an exception for this case, and possibly for any case involving slicing - I don't think it was designed to be correct in the presence of LIMIT/OFFSET. However, for the moment probably best to limit to the distinct()+slicing()+exists() combination.