在mysql中的一组行的累积总和

use*_*577 5 mysql sql join cumulative-sum

我有一个复杂的查询(包含多个连接,联合),它返回一组包含id,day,hr,amount的行.查询的输出如下所示:

id day    hr  amount 
1   1      1   10       
1   1      2   25       
1   1      3   30        
1   2      1   10       
1   2      2   40       
1   2      2   30        
2   1      1   10       
2   1      2   15        
2   1      3   30       
2   2      1   10       
2   2      2   20      
2   2      2   30  
Run Code Online (Sandbox Code Playgroud)

我需要找到每个小时的累计总数,每天的每个小时.输出应该是这样的:

id day    hr  amount cumulative total
1   1      1   10       10
1   1      2   25       35
1   1      3   30       65 
1   2      1   10       10
1   2      2   40       50
1   2      2   30       80 
2   1      1   10       10
2   1      2   15       25 
2   1      3   30       55
2   2      1   10       10
2   2      2   20       30
2   2      2   30       60
Run Code Online (Sandbox Code Playgroud)

我生成第一个输出的初始查询如下所示:

select id, day, hr, amount from
( //multiple joins on multiple tables)a
left join
(//unions on multiple tables)b
on a.id=b.id;
Run Code Online (Sandbox Code Playgroud)

什么是sql查询来获取第二个输出中描述的累积和?SET不应该在解决方案中使用.

谢谢.

spe*_*593 9

MySQL不提供用于获取正在运行的"累积和"的类型分析函数,就像其他DBMS(如Oracle或SQL Server)中提供的分析函数一样.

但是,可以使用MySQL模拟一些分析函数.

有(至少)两种可行的方法:

一种是使用相关子查询来获得小计.这种方法在大型集合上可能很昂贵,如果外部查询的谓词很复杂,则会很复杂.这实际上取决于"多个表上的多个连接"的复杂程度.(不幸的是,MySQL也不支持CTE.)

另一种方法是利用MySQL用户变量,做一些控制中断处理.这里的"技巧"是对查询的结果进行排序(使用ORDER BY),然后将查询包装在另一个查询中.

我将举一个后一种方法的例子.

因为MySQL的执行操作的顺序,将OVER列需要从价值之前被计算cumulative_totalid从当前行被保存到用户变量.把这个专栏放在首位是最容易的.

作为i(在下面的查询中)别名的内联视图就是用于初始化用户变量,以防万一这些已经在会话中设置.如果已经分配了值,我们希望忽略它们的当前值,最简单的方法是初始化它们.

您的原始查询将包含在括号中,并day在下面的示例中给出别名.对原始查询的唯一更改是添加了ORDER BY子句,因此我们可以确保按顺序处理查询中的行.

外部选择检查当前行的cid值是否与前一行"匹配".如果他们这样做,我们将day当前行添加到累计小计.如果它们不匹配,那么我们将累积小计重置为零,并从当前行添加金额(或者更简单地说,只是从当前行分配金额).

在我们完成累计总计算之后,我们将当前行中的amountid值保存到用户变量中,因此当我们处理下一行时它们可用.

例如:

SELECT IF(@prev_id = c.id AND @prev_day = c.day
         ,@cumtotal := @cumtotal + c.amount
         ,@cumtotal := c.amount) AS cumulative_total
     , @prev_id  := c.id  AS `id`
     , @prev_day := c.day AS `day`
     , c.hr
     , c.amount AS `amount'
  FROM ( SELECT @prev_id  := NULL
              , @prev_day := NULL
              , @subtotal := 0
       ) i
  JOIN (

         select id, day, hr, amount from
         ( //multiple joins on multiple tables)a
         left join
         (//unions on multiple tables)b
         on a.id=b.id

         ORDER BY 1,2,3
       ) c
Run Code Online (Sandbox Code Playgroud)

如果需要以不同的顺序返回列,并将累计总数作为最后一列,那么一个选项是将整个语句包装在一组parens中,并将该查询用作内联视图:

SELECT d.id
     , d.day
     , d.hr
     , d.amount
     , d.cumulative_total
FROM (
       // query from above
     ) d
Run Code Online (Sandbox Code Playgroud)


Luk*_*der 5

如果您使用的是 MySQL 8 或更高版本,则应该为此使用窗口函数。您的查询将显示为:

SELECT
  id, day, hr, amount,
  SUM (amount) OVER (PARTITION BY id, day ORDER BY hr) AS `cumulative total`
FROM t
Run Code Online (Sandbox Code Playgroud)

t您的桌子在哪里b连接到a. 一些注意事项:

  • PARTITION BY条款保证你得到一个累计idday,所以每一天,我们重新开始求和
  • ORDER BY条款定义了累积应该发生的顺序