在不使用原始数据的情况下聚合联合查询的结果

Sat*_*nix 5 django django-orm

我有一张看起来像这样的桌子

date                car_crashes         city
01.01               1                   Washington
01.02               4                   Washington
01.03               0                   Washington
01.04               2                   Washington
01.05               0                   Washington
01.06               3                   Washington
01.07               4                   Washington
01.08               1                   Washington
01.01               0                   Detroit
01.02               2                   Detroit
01.03               4                   Detroit
01.04               2                   Detroit
01.05               0                   Detroit
01.06               3                   Detroit
01.07               1                   Detroit
Run Code Online (Sandbox Code Playgroud)

我想知道全国每天发生多少次车祸,我可以用这个来做:

Model.values("date") \
.annotate(car_crashes=Sum('car_crashes')) \
.values("date", "car_crashes")
Run Code Online (Sandbox Code Playgroud)

现在,让我们假设我有一个这样的数组:

weights = [
    {
        "city": "Washington",
        "weight": 1,
    },
    {
        "city": "Detroit",
        "weight": 2,
    }
]
Run Code Online (Sandbox Code Playgroud)

这意味着底特律的车祸应乘以 2,然后再与华盛顿的相加。

可以这样做:

from django.db.models import IntegerField


when_list = [When(city=w['city'], then=w['weight']) for w in weights]
case_params = {'default': 1, 'output_field': IntegerField()}


Model.objects.values('date') \
    .annotate(
        weighted_car_crashes=Sum(
            F('car_crashes') * Case(*when_list, **case_params)
    ))
Run Code Online (Sandbox Code Playgroud)

但是,这会生成非常慢的 SQL 代码,尤其是在引入更多属性和更大数组时。

另一个更快但仍然次优的解决方案是使用熊猫:

aggregated = false
for weight in weights:

 ag = Model.filter(city=w[‘city’]).values("date") \
 .annotate(car_crashes=Sum('car_crashes') * w[‘weight’]) \
 .values("date", "car_crashes")


 if aggregated is False:
 aggregated = ag
 else:
 aggregated = aggregated.union(ag)


aggregated = pd.DataFrame(aggregated)
if len(weights) > 1:
 aggregated = aggregated.groupby("date", as_index=False).sum(level=[1])
Run Code Online (Sandbox Code Playgroud)

这更快,但仍然不如在调用 pandas 之前,我获取aggregated.query字符串并用几行 SQL 将其包装时发生的速度快。

SELECT "date", sum("car_crashes") FROM (


// String from Python
str(aggregated.query)


) as "foo" GROUP BY "date"
Run Code Online (Sandbox Code Playgroud)

这在粘贴到我的数据库 SQL 中时非常有效。我可以在 Python/Django 中使用,.raw()但文档说在使用之前要询问,.raw()因为大多数东西都可以用 ORM 完成。

然而,我不明白如何。一旦我调用.union()了 2 个查询集,我就无法进一步聚合。

aggregated.union(ag).annotate(cc=Sum('car_crashes'))
Run Code Online (Sandbox Code Playgroud)

无法计算 Sum('car_crashes'): 'car_crashes' 是一个聚合

这可能与Django ORM有关还是我应该使用.raw()?