按查询分组并求和所有值

Mil*_*uri 4 sql eloquent laravel-query-builder

我在 SQL 中有这样的产品表:

productID   Type               Amount
1046          1                 4  
1046          1                 5  
1046          2                 10          
1047          1                 5           
1047          2                 3  
Run Code Online (Sandbox Code Playgroud)

如何进行查询,输出如下:

productID   Type          TotalTypeAmount    TotalPerProductID
1046          1                9                  19
1046          2                10                 19
1047          1                 5                  8
1047          2                 3                  8
Run Code Online (Sandbox Code Playgroud)

Cai*_*ard 13

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

\n\n
SELECT \n  productID,\n  Type,\n  SUM(Amount) AS TotalTypeAmount,\n  SUM(SUM(Amount)) OVER (PARTITION BY productID) as TotalPerProduct\nFROM \n  Product\nGROUP BY \n  productID,Type\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
\n\n

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

\n\n

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

\n\n
SELECT \n  productID,\n  Type,\n  SUM(Amount) AS TotalTypeAmount\nFROM \n  Product\nGROUP BY \n  productID,Type\n
Run Code Online (Sandbox Code Playgroud)\n\n

这可能会产生如下结果:

\n\n
id1, type1, 100\nid1, type2, 200\nid2, type1, 300\nid2, type2, 400\n
Run Code Online (Sandbox Code Playgroud)\n\n

我们可以看到,所有 id2 的总数id1300,所有 id2 的总数为700。我们可以引入一个窗口函数,仅通过productid对所有数据进行求和,并且它会产生重复求和的结果,而不是导致行数缩小,即:

\n\n
id1, type1, 100, 300\nid1, type2, 200, 300\nid2, type1, 300, 700\nid2, type2, 400, 700\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

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

\n\n
SELECT \n  productID,\n  Type,\n  SUM(Amount) AS TotalTypeAmount,\n  SUM(TotalTypeAmount) OVER (PARTITION BY productID) as TotalPerProduct\nFROM \n  Product\nGROUP BY \n  productID,Type\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

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

但它比需要的更复杂。

\n\n

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

\n\n
SUM(sum(\xe2\x80\xa6)) OVER(PARTITION BY \xe2\x80\xa6)\n\xe2\x80\xa6\ngroup by \xe2\x80\xa6 \n
Run Code Online (Sandbox Code Playgroud)\n\n
    \n
  • 里面的小写字母sum(amount)是“GROUP BY 所做的总和”,并且
  • \n
  • 外面大写的SUM(\xe2\x80\xa6) OVER(\xe2\x80\xa6)是“窗口函数完成的总和”
  • \n
\n\n

我们必须重复一遍,SUM(SUM(amount))因为在运行窗口函数时,group by 完成的总和(即我们想要总计的东西)没有自己的名称 - 它只是被称为SUM(amount)

\n