在SQL中,我可以对两个计数求和,例如
SELECT (
(SELECT count(*) FROM a WHERE val=42)
+
(SELECT count(*) FROM b WHERE val=42)
)
我如何使用Django ORM执行此查询?
我最近的是
a.objects.filter(val=42).order_by().values_list('id', flat=True).union(
b.objects.filter(val=42).order_by().values_list('id', flat=True)
).count()
如果返回的计数很小,此方法很好,但是如果数据库必须在内存中保留很多行以对其进行计数,则这看起来很糟糕。
您的解决方案只能由values('pk')
而不是values_list('id', flat=True)
简化,因为这只会影响输出的一种行类型,但是两个查询集的源SQL相同:
SELECT id FROM a WHERE val=42 UNION SELECT id FROM b WHERE val=42
并且方法.count()
仅对子查询进行查询:
SELECT COUNT(*) FROM (... subquery ...)
数据库后端不必将所有值都保存在内存中。它也只能算他们而忘记。 (未选中)
类似地,如果您运行简单的SELECT COUNT(id) FROM a
,则无需收集id
。
在较大的查询中,形式为SELECT count(*) FROM a WHERE val=42
的子查询是不可能的,因为Django不会对聚合使用惰性评估,而是立即对其进行评估。
例如,可以推迟评估通过按仅具有一个可能值的某个表达式进行分组,例如GROUP BY (i >= 0)
(或通过外部引用,如果可行的话),但查询计划可能更糟。
[另一个问题是没有表就不可能有SELECT
。因此,我将在查询基础中使用不重要表的不重要行。
示例:
qs = Unimportant.objects.filter(pk=unimportant_pk).values('id').annotate(
total_a=a.objects.filter(val=42).order_by().values('val')
.annotate(cnt=models.Count('*')).values('cnt'),
total_b=b.objects.filter(val=42).order_by().values('val')
.annotate(cnt=models.Count('*')).values('cnt')
)
不好,但是可以很容易地并行化
SELECT
id,
(SELECT COUNT(*) AS cnt FROM a WHERE val=42 GROUP BY val) AS total_a,
(SELECT COUNT(*) AS cnt FROM b WHERE val=42 GROUP BY val) AS total_b
FROM unimportant WHERE id = unimportant_pk
Django文档确认不存在简单的解决方案。
Using aggregates within a Subquery expression ... ...这是在子查询中执行聚合的唯一方法,因为使用aggregate()尝试评估查询集(并且如果存在OuterRef,将无法解决)。