提问人:Ehsan 提问时间:11/9/2023 最后编辑:Ehsan 更新时间:11/9/2023 访问量:37
Django ORM 聚合与条件 SUM 与组失败
Django ORM aggregation with conditional SUM with group by failing
问:
我正在尝试在 Django ORM 中执行以下查询,但到目前为止没有运气。
我想将所有 PotatoesMeasurement 分组,并对它何时大于、小于和在某些值范围内进行有条件的计数。我有原始的mongosh查询来获得正确的结果,但我无法在django ORM中获得等效的结果。wn
alternaria
我的模型如下所示:
class BasicEntity(models.Model):
class Meta:
abstract = True
id = models.UUIDField(default=uuid4, editable=False, db_index=True, primary_key=True)
created_on = models.DateTimeField(auto_now_add=True, editable=False, blank=True)
updated_on = models.DateTimeField(auto_now=True, editable=False, blank=True)
class PotatoesMeasurement(BasicEntity):
class Meta:
db_table = "measurements_potatoes"
colorado_potato_beetle_larvae = models.FloatField( default=0.0,
validators=[MinValueValidator(0), MaxValueValidator(100)])
aphids_per_leaflet = models.IntegerField(blank=False, null=False)
late_blight = models.FloatField( default=0.0,
validators=[MinValueValidator(0), MaxValueValidator(100)])
alternaria = models.FloatField( default=0.0,
validators=[MinValueValidator(0), MaxValueValidator(100)])
wn = models.IntegerField(default=datetime.now().isocalendar().week,
validators=[MinValueValidator(1), MaxValueValidator(53)])
Mongosh查询完美运行:
db.measurements_potatoes.aggregate([
{
$group: {
_id: "$wn",
countSmaller: { $sum: { $cond: [{ $lte: ["$alternaria", 1] }, 1, 0] } },
countRange: { $sum: { $cond: [{ $range: [ 20, "$alternaria", 30 ] }, 1, 0] } },
countBigger: { $sum: { $cond: [{ $gt: ["$alternaria", 90] }, 1, 0] } }
}
},
{$sort: {_id: 1}},
]);
到目前为止,在 django ORM 方面,我有以下内容:
res = (
PotatoesMeasurement.objects.all()
.values("wn")
.aggregate(
countSmaller=Sum(
Case(When(alternaria__lt=1, then=1), default=0, output_field=IntegerField())
),
countRange=Sum(
Case(
When(alternaria__range=[30, 50], then=1),
default=0,
output_field=IntegerField(),
)
),
countBigger=Sum(
Case(When(alternaria__gt=1, then=1), default=0, output_field=IntegerField())
),
)
.order_by("wn")
)
上述命令失败,并出现以下错误:
raise exe from e
djongo.exceptions.SQLDecodeError:
Keyword: None
Sub SQL: None
FAILED SQL: SELECT SUM(CASE WHEN "measurements_potatoes"."alternaria" < %(0)s THEN %(1)s ELSE %(2)s END) AS "countSmaller", SUM(CASE WHEN "measurements_potatoes"."alternaria" BETWEEN %(3)s AND %(4)s THEN %(5)s ELSE %(6)s END) AS "countRange", SUM(CASE WHEN "measurements_potatoes"."alternaria" > %(7)s THEN %(8)s ELSE %(9)s END) AS "countBigger" FROM "measurements_potatoes"
Params: (1.0, 1, 0, 30.0, 50.0, 1, 0, 1.0, 1, 0)
Version: 1.3.6
The above exception was the direct cause of the following exception:
知道查询失败的原因吗?或者也许有其他方法可以执行原始查询?
谢谢
答:
你的 Django ORM 查询将失败,因为 和 一起使用不正确。.aggregate()
.order_by()
.aggregate()
是一个 QuerySet 方法,用于计算整个 QuerySet 的聚合。重要的是,它本身不返回 QuerySet,而是返回一个 Dictionary(参见文档)。
由于它也是一个 QuerySet 方法,而不是一个 Dictionary 方法,因此你不能在 之后链接它。.order_by()
.aggregate()
相反,如果要按 wn 分组并保留 QuerySet 来排序,则应使用 if you want to group。.annotate()
因此,更正的方法涉及使用来计算每组的条件和,然后根据需要对结果使用。.annotate()
.order_by()
下面介绍如何相应地重构查询:
from django.db.models import Sum, Case, When, IntegerField
res = (
PotatoesMeasurement.objects
.values('wn')
.annotate(
countSmaller=Sum(
Case(
When(alternaria__lte=1, then=1),
default=0,
output_field=IntegerField()
)
),
countRange=Sum(
Case(
When(alternaria__gte=20, alternaria__lt=30, then=1),
default=0,
output_field=IntegerField()
)
),
countBigger=Sum(
Case(
When(alternaria__gt=90, then=1),
default=0,
output_field=IntegerField()
)
)
)
.order_by('wn')
)
请注意,我已将条件更改为使用范围的 __gte 和 __lt。countRange
现在,对于抛出的SQLDecodeError,如果上述方法不能解决问题(希望它能解决问题),那么Djongo将这些查询转换为MongoDB的聚合框架的能力可能会受到限制(Djongo是您似乎正在使用的MongoDB的Django ORM包装器)。
恐怕我不是MongoDB专家,所以如果错误继续出现,我无法提供帮助。但希望对你的查询进行上述更改意味着你将向 Djongo 发送一个纯 QuerySet,并且希望它应该可以转换为 MongoDB 查询,因此你的错误应该得到解决。
评论
order_by
aggregate
评论