通过连接进行嵌套聚合,数字仍然膨胀

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进行聚合检索 (分组统计)

通过适配器膨胀 json 数据

如何根据特定的值进行聚合,并创建给定对象列表的嵌套地图?

MongoDB聚合和嵌套数字数组

python使用pandas通过聚合获取时序数据的最后一个指标数据(例如长度指标时间指标)生成标签并与原表连接(join)进行不同标签特征的可视化分析