Django 2.2.12
Python 3.7.5
Postgres 10.11

I am receiving what I perceive to be an incorrect SQL query from a Django 
queryset.  I am hoping I've just done something wrong.

Here's the setup:

from django.db.models import DecimalField, DO_NOTHING, F, ForeignKey, Model, 
Q, Sum, TextField


class TableA(Model):
    name = TextField()

    class Meta:
        db_table = "tablea"


class TableB(Model):
    tablea = ForeignKey(TableA, DO_NOTHING, null=True)
    value = DecimalField(max_digits=5, decimal_places=2)

    class Meta:
        db_table = "tableb"

Here's some sample data:

insert into tablea (id, name)
select * from (values (1, 'NAME 1'), (2, 'NAME 2'), (3, 'NAME 3')) t;

insert into tableb (id, tablea_id, value)
select * from (values
    (1, 1, 0), (2, 1, 5),
    (3, null, 13),
    (4, 2, 0), (5, 2, 0),
    (6, 3, -1), (7, 3, 1)
) t;

The goal is to group names from tablea and sum values from tableb where 
value != 0.  If I were to write it purely in SQL I'd probably do something 
like this:

select   a.name, sum(b.value)
from     tablea a inner join tableb b on b.tablea_id = a.id
where    b.value != 0
group by a.name;

The answer should be:

NAME 3     0
NAME 1     5

If I write the Django queryset as one of the following:

qs = (
    TableA.objects.filter(~Q(tableb__value=0))
    .annotate(the_name=F("name"), the_sum=Sum("tableb__value"))
    .values("the_name", "the_sum")
)

qs = (
    TableA.objects.exclude(tableb__value=0)
    .annotate(the_name=F("name"), the_sum=Sum("tableb__value"))
    .values("the_name", "the_sum")
)

I get SQL that I really didn't expect:

SELECT   "tablea"."name" AS "the_name",
         SUM("tableb"."value") AS "the_sum"
FROM     "tablea"
         LEFT OUTER JOIN "tableb" ON ("tablea"."id" = "tableb"."tablea_id")
WHERE    NOT (
            "tablea"."id" IN (
                SELECT  U1."tablea_id"
                FROM    "tableb" U1
                WHERE   (U1."value" = 0 AND U1."tablea_id" IS NOT NULL)
            )
         )
GROUP BY "tablea"."id";

which returns the wrong answer:

NAME 3     0

If I rewrite the queryset as such, I get the correct answer:

qs = (
    TableA.objects.filter(Q(tableb__value__gt=0) | Q(tableb__value__lt=0))
    .annotate(the_name=F("name"), the_sum=Sum("tableb__value"))
    .values("the_name", "the_sum")
)

This translates to:

SELECT   "tablea"."name" AS "the_name",
         SUM("tableb"."value") AS "the_sum"
FROM     "tablea"
         INNER JOIN "tableb" ON ("tablea"."id" = "tableb"."tablea_id")
WHERE    "tableb"."value" > 0 OR "tableb"."value" < 0
GROUP BY "tablea"."id";

Am I doing something wrong in my ~Q/exclude querysets?  Is this working as 
intended?

Thanks!

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-users+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-users/4cc2f0e6-00ff-47f8-88f0-03705305d23b%40googlegroups.com.

Reply via email to