【问题标题】:group by query with sum all value按查询分组,总和所有值
【发布时间】:2017-12-23 00:54:25
【问题描述】:

我在 SQL 中有 Product 表,如下所示:

productID   Type               Amount
1046          1                 4  
1046          1                 5  
1046          2                 10          
1047          1                 5           
1047          2                 3  

如何查询,输出如下:

productID   Type          TotalTypeAmount    TotalPerProductID
1046          1                9                  19
1046          2                10                 19
1047          1                 5                  8
1047          2                 3                  8

【问题讨论】:

  • TotalAllType 名称错误。应该是 TotalPerProductID,不是吗?
  • @CaiusJard 没错!
  • 对于 1046 和 1047 有类型 1 和 2 但为什么只显示 1046
  • 这可以通过只扫描表一次(不加入)来解决 - 请参阅我发布的示例

标签: sql eloquent laravel-query-builder


【解决方案1】:

一种更紧凑/更专业的方式,也应该更快,因为它不需要加入:

SELECT 
  productID,
  Type,
  SUM(Amount) AS TotalTypeAmount,
  SUM(SUM(Amount)) OVER (PARTITION BY productID) as TotalPerProduct
FROM 
  Product
GROUP BY 
  productID,Type

http://sqlfiddle.com/#!6/ca308/4


SUM(amount) 是每个 productid,type 的金额总和,即如果您刚刚执行 GROUP BY,您将获得的总和,因此每个 productid 将有多个不同的总和,因为它们被分解为 type亚组。 SUM(SUM(amount)) PARTITION BY(productid) 是“sum_per_productid_and_type 仅按 productid 分组的总和”

假设我们有更简单的查询:

SELECT 
  productID,
  Type,
  SUM(Amount) AS TotalTypeAmount
FROM 
  Product
GROUP BY 
  productID,Type

这可能会产生如下结果:

id1, type1, 100
id1, type2, 200
id2, type1, 300
id2, type2, 400

我们可以看到所有id1 的总数为300,所有id2 的总数为700。我们可以引入一个窗口函数来仅按 productid 对所有数据求和,它会在求和重复的地方产生结果,而不是导致行数减少,即:

id1, type1, 100, 300
id1, type2, 200, 300
id2, type1, 300, 700
id2, type2, 400, 700

这是因为窗口函数会进行分组/求和,然后将结果应用于每一行。

重要的是要了解窗口操作是在分组和求和完成之后执行的,但在我们在 SELECT 中为列指定任何别名之前。如果在窗口函数运行之前已经分配了列名,那么我们可以说SUM(TotalTypeAmount) 而不是SUM(SUM(Amount))

SELECT 
  productID,
  Type,
  SUM(Amount) AS TotalTypeAmount,
  SUM(TotalTypeAmount) OVER (PARTITION BY productID) as TotalPerProduct
FROM 
  Product
GROUP BY 
  productID,Type

但这是一个语法错误,因为我们不能在定义它的同一个 SELECT 中引用 TotalTypeAmount。我们可以通过使用子查询来实现这一点:

SELECT
  x.productID,
  x.Type,
  x.TotalTypeAmount, --here we use it after it has been defined in the subquery
  SUM(x.TotalTypeAmount) OVER (PARTITION BY x.productID) as TotalPerProduct
FROM
(
  SELECT 
    productID,
    Type,
    SUM(Amount) AS TotalTypeAmount --here we define it
  FROM 
    Product
  GROUP BY 
    productID,Type
) x

但它比它需要的更复杂。

本质上,虽然看起来很奇怪,但我们只需要在看到时记住:

SUM(sum(…)) OVER(PARTITION BY …)
…
group by … 
  • 小写的内部sum(amount) 是“GROUP BY 完成的总和”,并且
  • 大写的外部SUM(…) OVER(…)是“窗口函数完成的总和”

我们不得不重复自己并说SUM(SUM(amount)),因为在运行窗口函数时,group by 完成的总和(即我们想要总计的东西)没有自己的名字 - 它刚刚被称为SUM(amount)

【讨论】:

  • 很好的答案。为什么需要 sum(sum())?我注意到没有它就行不通。
【解决方案2】:

这样我们也可以做到

   Select 
T.productID,
T.Type,
T.Tamount,
TT.FAmount
     from (
select 
        productID, 
        Type, 
        SUM(Amount)Tamount 
from 
    Product
GROUP BY  productID, Type
)T
INNER JOIN (
select 
    productID,
    SUM(Amount)FAmount 
from 
    Product
GROUP BY  productID
)TT
ON T.productID = TT.productID

【讨论】:

  • 对分组查询使用窗口函数会更好
【解决方案3】:

如果您使用的是 SQL Server 或 Postgres,则以下内容应该可以工作:

WITH CTE
AS
(
SELECT 
productID,
SUM(Amount) AS TotalAllType
FROM Product
GROUP BY productID
)

SELECT 
productID,
Type,
SUM(Amount) AS TotalTypeAmount
,TotalAllType
FROM Product AS P
LEFT JOIN CTE ON CTE.productID=P.productID
GROUP BY productID,Type, TotalAllType;

【讨论】:

    猜你喜欢
    • 2013-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多