我有一个复杂的查询(包含多个连接,联合),它返回一组包含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。
感谢。
答案 0 :(得分:7)
MySQL不提供用于获取正在运行的“累积和”的类型分析函数,例如其他DBMS(如Oracle或SQL Server)中可用的分析函数。
但是,可以使用MySQL模拟一些分析函数。
(至少)有两种可行的方法:
一种是使用相关子查询来获得小计。这种方法在大型集合上可能很昂贵,如果外部查询的谓词很复杂,则会很复杂。这实际上取决于“多个表上的多个连接”的复杂程度。 (不幸的是,MySQL也不支持CTE。)
另一种方法是利用MySQL用户变量,做一些控制中断处理。这里的“技巧”是对查询的结果进行排序(使用ORDER BY),然后将查询包装在另一个查询中。
我将举一个后一种方法的例子。
由于MySQL执行操作的顺序,需要在将来自当前行的cumulative_total
和id
的值保存到用户变量之前计算day
列。把这个专栏放在第一位是最容易的。
作为i(在下面的查询中)别名的内联视图就是初始化用户变量,以防万一这些已经在会话中设置。如果那些已经分配了值,我们想要忽略它们的当前值,最简单的方法是初始化它们。
您的原始查询将包含在括号中,并在下面的示例中被赋予别名c
。对原始查询的唯一更改是添加了ORDER BY子句,因此我们可以确保按顺序处理查询中的行。
外部选择检查当前行的id
和day
值是否与前一行“匹配”。如果他们这样做,我们将当前行的amount
添加到累计小计。如果它们不匹配,那么我们将累计小计重置为零,并从当前行添加金额(或者更简单地说,只是从当前行分配金额)。
在我们完成累计总计算之后,我们将当前行的id
和day
值保存到用户变量中,因此当我们处理下一行时它们可用。
例如:
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
如果需要以不同的顺序返回列,并将累计总数作为最后一列,那么一个选项是将整个语句包装在一组parens中,并将该查询用作内联视图:
SELECT d.id
, d.day
, d.hr
, d.amount
, d.cumulative_total
FROM (
// query from above
) d
答案 1 :(得分:1)
如果您使用的是MySQL 8或更高版本,则应使用window functions。您的查询将显示为:
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
子句定义了应按哪种顺序进行累积答案 2 :(得分: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;