7

我有一个复杂的查询(包含多个联接、联合),它返回一组包含 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  

我需要为一天中的每个小时找到每个 id 的累积总数。输出应该是这样的:

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

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

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

如第二个输出中所述,获取累积总和的 sql 查询是什么?SET 不应在解决方案中使用。

谢谢。

4

3 回答 3

12

更新

MySQL 8.0 引入了“窗口函数”,相当于 SQL Server“窗口函数”的功能(具有 Transact-SQLOVER语法提供的分区和排序)和 Oracle“分析函数”。

MySQL 参考手册 12.21 窗口函数https://dev.mysql.com/doc/refman/8.0/en/window-functions.html

这里提供的答案是 8.0 之前的 MySQL 版本的一种方法。


原始答案

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

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

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

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

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

我将举例说明后一种方法。

由于 MySQL 执行操作的顺序,需要在将来自和来自当前行cumulative_total的值保存到用户变量之前计算列。把这个专栏放在第一位是最简单的。idday

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

在下面的示例中,您的原始查询被括在括号中,并被赋予了一个别名c。对原始查询的唯一更改是添加了 ORDER BY 子句,因此我们可以确保按顺序处理查询中的行。

外部选择检查当前行中的idday值是否“匹配”前一行。如果是这样,我们将amount当前行中的值添加到累积小计中。如果它们不匹配,那么我们将累积小计重置为零,并添加当前行的金额(或者,更简单地说,只需分配当前行的金额)。

在我们完成累计总数的计算之后,我们将当前行中的idday值保存到用户变量中,以便在处理下一行时可以使用它们。

例如:

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

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

SELECT d.id
     , d.day
     , d.hr
     , d.amount
     , d.cumulative_total
FROM (
       // query from above
     ) d
于 2013-07-15T21:59:26.813 回答
6

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

SELECT
  id, day, hr, amount,
  SUM (amount) OVER (PARTITION BY id, day ORDER BY hr) AS `cumulative total`
FROM t

t你的表在哪里b加入a。一些注意事项:

  • PARTITION BY子句保证您得到一个 和 的累积总和,所以每天,我们都会重新开始id求和day
  • ORDER BY子句定义了累积应该发生的顺序
于 2018-09-17T18:10:54.010 回答
0

给你,这是你的高潮总...

select f1.id, f1.day, f1.hr, f1.amount, sum(f2.amount) as culminative_total from foo f1
 inner join foo f2 on (f1.day = f2.day and f1.id=f2.id)
 where f2.hr <= f1.hr
 group by f1.id, f1.day, f1.hour;
于 2013-07-15T22:55:13.210 回答