按查询分组并求和所有值

group by query with sum all value

我在 SQL 中有产品 table 像这样:

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

如果您使用的是 SQL 服务器或 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;

这样我们也可以做到

   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

一种更 compact/professional 的方式,应该也更快,因为它不需要连接:

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) 是 "the sum of sum_per_productid_and_type grouped by productid only"

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

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。我们可以引入一个window函数,只按productid对所有数据进行求和,它会产生重复求和的结果,而不是导致行数减少,即:

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

这是因为 window 函数执行 grouping/summing 然后将结果应用于每一行。

重要的是要理解 window 操作是在分组和求和完成之后执行的,但在我们为列提供 SELECT 中的任何别名之前。如果在 window 函数是 运行 之前分配了列名,那么我们可以说 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)是"the sum that is done by the GROUP BY",
  • 大写的外层SUM(…) OVER(…)是"the sum that is done by the window function"

我们不得不重复自己并说 SUM(SUM(amount)) 因为当时 window 函数是 运行,group by 完成的总和(即我们想要的东西total up) 没有自己的名字 - 它只是被称为 SUM(amount)