Django queryset 值联合中的奇怪行为
Weird behavior in Django queryset union of values
我想将用户的相关值与没有这些值的用户相加。
这是我的模型结构的简化版本:
class Answer(models.Model):
person = models.ForeignKey(Person)
points = models.PositiveIntegerField(default=100)
correct = models.BooleanField(default=False)
class Person(models.Model):
# irrelevant model fields
示例数据集:
Person | Answer.Points
------ | ------
3 | 50
3 | 100
2 | 100
2 | 90
Person 4 has no answers and therefore, points
通过下面的查询,我可以实现每个人的积分总和:
people_with_points = Person.objects.\
filter(answer__correct=True).\
annotate(points=Sum('answer__points')).\
values('pk', 'points')
<QuerySet [{'pk': 2, 'points': 190}, {'pk': 3, 'points': 150}]>
但是,由于有些人可能没有任何相关的 Answer
条目,他们将有 0 分,并且在下面的查询中我使用 Coalesce
到 "fake" 他们的分数,就像这样:
people_without_points = Person.objects.\
exclude(pk__in=people_with_points.values_list('pk')).\
annotate(points=Coalesce(Sum('answer__points'), 0)).\
values('pk', 'points')
<QuerySet [{'pk': 4, 'points': 0}]>
这两个都按预期工作,但我想将它们放在同一个查询集中,所以我使用联合运算符 |
来加入它们:
everyone = people_with_points | people_without_points
现在,对于问题:
在此之后,没有积分的人的points
值将变为None
而不是0。
<QuerySet [{'pk': 2, 'points': 190}, {'pk': 3, 'points': 150}, {'pk': 4, 'points': None}]>
有人知道为什么会这样吗?
谢谢!
我应该提到我可以通过再次注释查询集并将空值合并为 0 来解决这个问题,如下所示:
everyone.\
annotate(real_points=Concat(Coalesce(F('points'), 0), Value(''))).\
values('pk', 'real_points')
<QuerySet [{'pk': 2, 'real_points': 190}, {'pk': 3, 'real_points': 150}, {'pk': 4, 'real_points': 0}]>
但我想了解为什么 union 不能像我在原来的问题中预期的那样工作。
编辑:
我想我明白了。一位朋友指示我使用 django-debug-toolbar
检查我的 SQL 查询以进一步调查这种情况,我发现了以下内容:
因为它是两个查询的并集,所以不考虑第二个查询注释并且不使用 COALESCE
到 0。通过将其移动到第一个查询,它会传播到第二个查询,我可以获得预期的结果。
基本上,我更改了以下内容:
# Moved the "Coalesce" to the initial query
people_with_points = Person.objects.\
filter(answer__correct=True).\
annotate(points=Coalesce(Sum('answer__points'), 0)).\
values('pk', 'points')
# Second query does not have it anymore
people_without_points = Person.objects.\
exclude(pk__in=people_with_points.values_list('pk')).\
values('pk', 'points')
# We will have the values with 0 here!
everyone = people_with_points | people_without_points
我想将用户的相关值与没有这些值的用户相加。
这是我的模型结构的简化版本:
class Answer(models.Model):
person = models.ForeignKey(Person)
points = models.PositiveIntegerField(default=100)
correct = models.BooleanField(default=False)
class Person(models.Model):
# irrelevant model fields
示例数据集:
Person | Answer.Points
------ | ------
3 | 50
3 | 100
2 | 100
2 | 90
Person 4 has no answers and therefore, points
通过下面的查询,我可以实现每个人的积分总和:
people_with_points = Person.objects.\
filter(answer__correct=True).\
annotate(points=Sum('answer__points')).\
values('pk', 'points')
<QuerySet [{'pk': 2, 'points': 190}, {'pk': 3, 'points': 150}]>
但是,由于有些人可能没有任何相关的 Answer
条目,他们将有 0 分,并且在下面的查询中我使用 Coalesce
到 "fake" 他们的分数,就像这样:
people_without_points = Person.objects.\
exclude(pk__in=people_with_points.values_list('pk')).\
annotate(points=Coalesce(Sum('answer__points'), 0)).\
values('pk', 'points')
<QuerySet [{'pk': 4, 'points': 0}]>
这两个都按预期工作,但我想将它们放在同一个查询集中,所以我使用联合运算符 |
来加入它们:
everyone = people_with_points | people_without_points
现在,对于问题:
在此之后,没有积分的人的points
值将变为None
而不是0。
<QuerySet [{'pk': 2, 'points': 190}, {'pk': 3, 'points': 150}, {'pk': 4, 'points': None}]>
有人知道为什么会这样吗?
谢谢!
我应该提到我可以通过再次注释查询集并将空值合并为 0 来解决这个问题,如下所示:
everyone.\
annotate(real_points=Concat(Coalesce(F('points'), 0), Value(''))).\
values('pk', 'real_points')
<QuerySet [{'pk': 2, 'real_points': 190}, {'pk': 3, 'real_points': 150}, {'pk': 4, 'real_points': 0}]>
但我想了解为什么 union 不能像我在原来的问题中预期的那样工作。
编辑:
我想我明白了。一位朋友指示我使用 django-debug-toolbar
检查我的 SQL 查询以进一步调查这种情况,我发现了以下内容:
因为它是两个查询的并集,所以不考虑第二个查询注释并且不使用 COALESCE
到 0。通过将其移动到第一个查询,它会传播到第二个查询,我可以获得预期的结果。
基本上,我更改了以下内容:
# Moved the "Coalesce" to the initial query
people_with_points = Person.objects.\
filter(answer__correct=True).\
annotate(points=Coalesce(Sum('answer__points'), 0)).\
values('pk', 'points')
# Second query does not have it anymore
people_without_points = Person.objects.\
exclude(pk__in=people_with_points.values_list('pk')).\
values('pk', 'points')
# We will have the values with 0 here!
everyone = people_with_points | people_without_points