#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 , 13 years ago
| Cc: | added |
|---|
comment:2 by , 13 years ago
| Triage Stage: | Unreviewed → Accepted |
|---|
comment:3 by , 13 years ago
| Cc: | added |
|---|
comment:4 by , 13 years ago
| Owner: | changed from to |
|---|---|
| Status: | new → assigned |
comment:5 by , 13 years ago
| Has patch: | set |
|---|
Here is my pull request: https://github.com/django/django/pull/161
comment:6 by , 13 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 , 12 years ago
| Patch needs improvement: | set |
|---|
comment:8 by , 12 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 , 12 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.