在一个查询中对来自不同表的数值求和
Sum numeric values from different tables in one query
在 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
没有 table 是不可能的。因此,我将在查询基础中使用不重要 table 的不重要行。
示例:
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
...
... This is the only way to perform an aggregation within a Subquery, as using aggregate() attempts to evaluate the queryset (and if there is an OuterRef, this will not be possible to resolve).
在 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
没有 table 是不可能的。因此,我将在查询基础中使用不重要 table 的不重要行。
示例:
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
...
... This is the only way to perform an aggregation within a Subquery, as using aggregate() attempts to evaluate the queryset (and if there is an OuterRef, this will not be possible to resolve).