通过连接进行嵌套聚合,数字仍然膨胀
Posted
技术标签:
【中文标题】通过连接进行嵌套聚合,数字仍然膨胀【英文标题】:Nested aggregation via join, figures still inflated 【发布时间】:2021-01-26 22:18:24 【问题描述】:我正在使用 Northwind 数据库以避免在聚合多个度量时出现裂痕/风扇陷阱问题。我正在尝试使用预聚合联接而不是全部联合。
- 这是一个测试,我正在汇总库存产品数量和订单数量。两者当然都有适当的非膨胀聚合,但是一旦我把所有东西放在一起,我就会得到膨胀的数字。
仅检索订单数量:
SELECT S.SupplierID,
S.Country,
sum(OD.Quantity) as OrderDetailsQuantity
FROM [Order Details] OD
JOIN [Products] P ON P.ProductID = OD.ProductID
JOIN [Suppliers] S ON S.SupplierID = P.SupplierID
WHERE P.SupplierID = 1
GROUP BY S.SupplierID,S.Country
ORDER BY S.SupplierID ASC
SupplierID Country OrderDetailsQuantity
----------- --------------- --------------------
1 UK 2213
仅检索库存产品的数量:
SELECT P.ProductID, P.SupplierID, sum(P.UnitsInStock) as ProductsUnitsInStock
FROM [Products] P
WHERE SupplierID = 1
GROUP BY P.ProductID, P.SupplierID
ProductID SupplierID ProductsUnitsInStock
----------- ----------- --------------------
1 1 39
2 1 17
3 1 13
因此,对于 SupplierId = 1,我们正在查看 69 件库存产品和 2213 个订单的总预期结果。现在,我首先快速检查一下,这看起来很好。
SELECT S.SupplierID,
S.Country,
sum(OD.Quantity) AS OrderDetailsQuantity,
test123.ProductsUnitsInStock
FROM [Order Details] OD,
( SELECT P.ProductID, P.SupplierID, sum(P.UnitsInStock) ProductsUnitsInStock
FROM Products P
GROUP BY P.ProductID, P.SupplierID) as test123
INNER JOIN Suppliers S ON S.SupplierID = test123.SupplierID -- filter aggregation results
WHERE OD.ProductID = test123.ProductID -- filter aggregation results
AND test123.SupplierID = 1
GROUP BY S.SupplierID, S.Country, test123.ProductsUnitsInStock
ORDER BY S.SupplierID ASC
SupplierID Country OrderDetailsQuantity ProductsUnitsInStock
----------- --------------- -------------------- --------------------
1 UK 328 13
1 UK 1057 17
1 UK 828 39
但现在我想汇总 ProductsUnitsInStock,但它失败了,最终有 2386 种产品库存。我哪里做错了?
SELECT S.SupplierID,
S.Country,
sum(OD.Quantity) AS OrderDetailsQuantity,
sum(test123.ProductsUnitsInStock) AS ProductsUnitsInStock
FROM [Order Details] OD,
( SELECT P.ProductID, P.SupplierID, sum(P.UnitsInStock) ProductsUnitsInStock
FROM Products P
GROUP BY P.ProductID, P.SupplierID) as test123
INNER JOIN Suppliers S ON S.SupplierID = test123.SupplierID -- filter aggregation results
WHERE OD.ProductID = test123.ProductID -- filter aggregation results
AND test123.SupplierID = 1
GROUP BY S.SupplierID, S.Country
ORDER BY S.SupplierID ASC
SupplierID Country OrderDetailsQuantity ProductsUnitsInStock
----------- --------------- -------------------- --------------------
1 UK 2213 2386
更新 CSteve,由于订单详细信息上的 1:N 关系,只需将问题移至聚合联接:
with
p_cte as (
select p.ProductID, p.SupplierID, s.Country, sum(p.UnitsInStock) as ProductsUnitsInStock
from Products p
join Suppliers s on p.SupplierID = s.SupplierID
group by p.ProductID, p.SupplierID, s.Country
),
oi_cte as (
SELECT OD.ProductID, P.SupplierID, s.Country, sum(OD.Quantity) AS OrderDetailsQuantity
FROM [Order Details] OD
join Products P on P.ProductID = OD.ProductID
join Suppliers S on P.SupplierID = P.SupplierID
GROUP BY OD.ProductID, P.SupplierID, s.Country
)
select coalesce(p.SupplierID, oi.SupplierID) SupplierID,
coalesce(p.Country, oi.Country) Country,
sum(oi.OrderDetailsQuantity) OrderDetailsQuantity,
sum(p.ProductsUnitsInStock) ProductsUnitsInStock
from p_cte p
full join oi_cte oi on p.ProductID=oi.ProductID
and p.SupplierID=oi.SupplierID
and p.Country = oi.Country
group by coalesce(p.SupplierID, oi.SupplierID),
coalesce(p.Country, oi.Country)
order by coalesce(p.SupplierID, oi.SupplierID);
【问题讨论】:
在编写可读代码表时,别名是必不可少的。请使用相关且简短的表别名 为什么要为后面的查询切换 old ANSI-89 JOIN 语法?另外,我确实建议重新考虑对象的名称[Order Details]
。尽量确保您提供不需要分隔标识的对象名称。一些(写得不好的)应用程序没有正确引用它们的对象名称,因此这些对象存在问题。
至于你哪里出错了,除了恢复到 ANSI-89 JOIN 语法之外,我认为如果不是你真正想要做什么> SUM
列 [Order Details].[Quantity]
在您的外部查询中,而不是将其放在 GROUP BY
中。
感谢 cmets。带有空格的表名是在 Northwind 数据库中定义的,我没有过多注意使用 ANSI-92。我实际上想对数量列求和,而不是将它放在 GROUP BY 中,因为我需要聚合。不是个别结果。我实际上是在第三个查询中这样做的,以查看结果是否会正确返回。只是第四个查询中的最后一个 SUM 提供了一个奇怪的结果。
我用别名更新了代码,希望这能帮助你们更好地阅读。
【参考方案1】:
如果我没看错,您希望按 SupplerID 和国家/地区汇总库存产品和订单。在下面代码中的 CTE 中,我还在摘要中包含了 ProductID(但未包含在最终的 SELECT 中)。由于似乎有些商品有库存但没有销售,反之亦然(不能排除),查询使用 FULL JOIN,然后 SUM 聚合函数忽略 NULL。像这样的
with
p_cte as (
select p.ProductID, p.SupplierID, s.Country,
sum(p.UnitsInStock) as ProductsUnitsInStock
from Products p
join Suppliers s on p.SupplierID=s.SupplierID
group by p.ProductID, p.SupplierID, s.Country),
oi_cte as (
select oi.ProductID, oi.SupplierID, s.Country,
sum(oi.Quantity) as OrderDetailsQuantity
from [Order Details] oi
join Suppliers s on oi.SupplierID = s.SupplierID)
select coalesce(p.SupplierID, oi.SupplierID) SupplierID,
coalesce(p.Country, oi.Country) Country,
sum(oi.OrderDetailsQuantity) OrderDetailsQuantity,
sum(p.ProductsUnitsInStock) ProductsUnitsInStock
from p_cte p
full join oi_cte oi on p.ProductID=oi.ProductID
and p.SupplierID=oi.SupplierID
and p.Country = oi.Country
group by coalesce(p.SupplierID, oi.SupplierID),
coalesce(p.Country, oi.Country)
order by coalesce(p.SupplierID, oi.SupplierID);
【讨论】:
感谢 SteveC,现在检查一下。将返回结果。 不幸的是,挑战发生了变化。为了获得供应商信息,我必须通过订单详细信息加入,因此我只需在聚合中引入另一个 1:N。最终结果仍然是夸大的数字。我用新的查询更新了我的帖子,稍微修改了一下。 似乎有些情况下每个产品有超过 1 个供应商。如何将供应商分配给订购的零件? 一个供应商可以提供多个产品,但一个产品只能有一个供应商。我只是将 Northwind 数据库(来自 Microsoft)用于测试目的,基本上我试图最终是我可以通过 C# 生成的这类情况的最佳实践。它是更大的概念证明的一部分,用户可以在其中选择度量、维度和切片,我们希望生成 SQL 代码以获得我们可以可视化的结果集。 Northwind 没有安装在我的任何实例上,如果每个产品只有 1 个供应商,我看不到行膨胀的原因。关于这些情况的最佳实践,通过 C# 生成 SQL 是向后 imo。我的项目使用 C# 中的高速过滤器来消除与运行存储过程相关的自定义代码。这么说吧,如果您创建接受 JSON 作为输入并返回 JSON 作为输出的参数化过程,那么使用 .NET Core 您几乎可以消除所有特定于过程的 C# 代码并使 HTTP 管道短路以上是关于通过连接进行嵌套聚合,数字仍然膨胀的主要内容,如果未能解决你的问题,请参考以下文章
如何通过不同级别的枢轴聚合然后在pyspark中进行内部连接?
ES 24 - 如何通过Elasticsearch进行聚合检索 (分组统计)
python使用pandas通过聚合获取时序数据的最后一个指标数据(例如长度指标时间指标)生成标签并与原表连接(join)进行不同标签特征的可视化分析