带注释条件表达式的 Django 查询使用 INNER JOIN。如何让它使用 OUTER JOIN?
Django query with annotated conditional expression uses INNER JOIN. How do I get it to use OUTER JOIN?
我有一个 "Meal" 模型,其外键指向 "Food"。每顿饭都有一个等级:好、坏或无所谓。我想查询所有食物的列表并注释每种类型的膳食评级的计数,但有些食物还没有膳食,所以我希望查询使用 LEFT OUTER JOIN,在这种情况下计数应该为零。
我在 Django 1.8 中使用条件表达式,它总是将关系切换为 "Food" 和 "Meal" 之间的 INNER JOIN。例如:
膳食模型:
class Meal(models.Model):
GOOD = 1
BAD = 2
INDIFFERENT = 3
RATING_CHOICES = (
(GOOD, 'Good'),
(BAD, 'Bad'),
(INDIFFERENT, 'Indifferent')
)
meal_time = models.DateTimeField()
food = models.ForeignKey("Food")
rating = models.IntegerField(blank=True, null=True, choices=RATING_CHOICES)
当我查询 Food.objects.annotate(total_meals=Count('meal'))
时,Django 会生成一个类似
的查询
SELECT ... FROM "Food"
LEFT OUTER JOIN "Meal" ON ...
GROUP BY "Food"
但是,当我添加这些条件注释时:
class FoodQuerySet(models.QuerySet):
def with_meal_rating_frequency(self):
return self.annotate(
total_meals=Count('meal'),
good_meals=Sum(
Case(When(meal__rating=Meal.GOOD, then=1),
output_field=models.IntegerField(), default=0)
),
bad_meals=Sum(
Case(When(meal__rating=Meal.BAD, then=1),
output_field=models.IntegerField(), default=0)
),
indifferent_meals=Sum(
Case(When(meal__rating=Meal.INDIFFERENT, then=1),
output_field=models.IntegerField(), default=0)
)
)
Django 使用 and INNER JOIN
代替。
SELECT ... FROM "Food"
INNER JOIN "Meal" ON ...
GROUP BY "Food"
我知道这个问题与 非常相似,但我不清楚如何将公认的解决方案应用于我的案例。如何让 Django 使用 LEFT OUTER JOIN?感谢您的帮助,谢谢!
我找到了一个到目前为止似乎有效的解决方案,使用 Count()
而不是 Sum()
并让条件检查 NULL 膳食,这将不包括在计数中:
class FoodQuerySet(models.QuerySet):
def with_meal_rating_frequency(self):
return self.annotate(
total_meals=Count('meal'),
good_meals=Count(
Case(When(Q(meal__isnull=True) | Q(meal__rating=Meal.GOOD), then='meal__rating'),
output_field=models.IntegerField(), default=None)
),
bad_meals=Count(
Case(When(Q(meal__isnull=True) | Q(meal__rating=Meal.BAD), then='meal__rating'),
output_field=models.IntegerField(), default=None)
),
indifferent_meals=Count(
Case(When(Q(meal__isnull=True) | Q(meal__rating=Meal.INDIFFERENT), then='meal__rating'),
output_field=models.IntegerField(), default=None)
)
)
我有一个 "Meal" 模型,其外键指向 "Food"。每顿饭都有一个等级:好、坏或无所谓。我想查询所有食物的列表并注释每种类型的膳食评级的计数,但有些食物还没有膳食,所以我希望查询使用 LEFT OUTER JOIN,在这种情况下计数应该为零。
我在 Django 1.8 中使用条件表达式,它总是将关系切换为 "Food" 和 "Meal" 之间的 INNER JOIN。例如:
膳食模型:
class Meal(models.Model):
GOOD = 1
BAD = 2
INDIFFERENT = 3
RATING_CHOICES = (
(GOOD, 'Good'),
(BAD, 'Bad'),
(INDIFFERENT, 'Indifferent')
)
meal_time = models.DateTimeField()
food = models.ForeignKey("Food")
rating = models.IntegerField(blank=True, null=True, choices=RATING_CHOICES)
当我查询 Food.objects.annotate(total_meals=Count('meal'))
时,Django 会生成一个类似
SELECT ... FROM "Food"
LEFT OUTER JOIN "Meal" ON ...
GROUP BY "Food"
但是,当我添加这些条件注释时:
class FoodQuerySet(models.QuerySet):
def with_meal_rating_frequency(self):
return self.annotate(
total_meals=Count('meal'),
good_meals=Sum(
Case(When(meal__rating=Meal.GOOD, then=1),
output_field=models.IntegerField(), default=0)
),
bad_meals=Sum(
Case(When(meal__rating=Meal.BAD, then=1),
output_field=models.IntegerField(), default=0)
),
indifferent_meals=Sum(
Case(When(meal__rating=Meal.INDIFFERENT, then=1),
output_field=models.IntegerField(), default=0)
)
)
Django 使用 and INNER JOIN
代替。
SELECT ... FROM "Food"
INNER JOIN "Meal" ON ...
GROUP BY "Food"
我知道这个问题与
我找到了一个到目前为止似乎有效的解决方案,使用 Count()
而不是 Sum()
并让条件检查 NULL 膳食,这将不包括在计数中:
class FoodQuerySet(models.QuerySet):
def with_meal_rating_frequency(self):
return self.annotate(
total_meals=Count('meal'),
good_meals=Count(
Case(When(Q(meal__isnull=True) | Q(meal__rating=Meal.GOOD), then='meal__rating'),
output_field=models.IntegerField(), default=None)
),
bad_meals=Count(
Case(When(Q(meal__isnull=True) | Q(meal__rating=Meal.BAD), then='meal__rating'),
output_field=models.IntegerField(), default=None)
),
indifferent_meals=Count(
Case(When(Q(meal__isnull=True) | Q(meal__rating=Meal.INDIFFERENT), then='meal__rating'),
output_field=models.IntegerField(), default=None)
)
)