MySQL实现支付余额累计求和问题排查及效率咨询
关于MySQL累计求和的子查询 vs 变量法问题解答
嘿,我来帮你捋清楚这两个问题——子查询日期相同出错的原因,以及两种方法的效率差异~
一、先解决子查询日期相同的错误问题
你说子查询法在日期相同时结果出错,大概率是因为你的子查询条件只用到了pd <= t.pd,当同一天有多条股息记录时,每条记录都会把当天所有的股息都累加一遍,导致重复计算。举个例子:如果2024-05-01有两条各100的股息,第一条子查询会算上两条100(累计200),第二条同样也会算上两条100(累计200),这就错了。
要修复这个问题,你需要给每条记录加一个唯一的排序依据(比如表的主键id,或者其他能区分同日期记录的字段),把子查询的条件改成:
SELECT t.pd, t.dividend_amount, (SELECT SUM(dividend_amount) FROM your_table WHERE pd < t.pd OR (pd = t.pd AND id <= t.id)) AS cumulative_balance FROM your_table t ORDER BY t.pd, t.id;
这样同日期的记录会按id顺序依次累加,就不会出现重复计算的问题了。
二、子查询法确实比变量法效率更低
从性能角度来说,子查询法的效率劣势很明显:
- 子查询是逐行执行的——每输出一行,MySQL就要重新执行一次内部的SUM查询,相当于对表做了n次扫描(n是表的行数),时间复杂度是O(n²)。
- 变量法是单次扫描——先把数据按顺序排好,然后用变量逐行累加,时间复杂度是O(n),只需要扫一遍表。
如果你的数据量很小(比如几百条),可能感觉不到差异,但当数据量达到几千、几万条时,子查询的速度会慢很多,尤其是如果没有给pd和id建立联合索引的话,每次子查询都会全表扫描,性能差距会被放大。
三、变量法的正确写法(避免踩坑)
变量法的关键是必须先对数据排序,因为MySQL的变量赋值依赖于结果集的顺序。正确的写法应该是:
SELECT pd, dividend_amount, @running_total := @running_total + dividend_amount AS cumulative_balance FROM -- 先按日期+唯一键排序,保证累加顺序正确 (SELECT pd, dividend_amount FROM your_table ORDER BY pd, id) AS sorted_data, -- 初始化累计变量 (SELECT @running_total := 0) AS init_var;
这样同日期的记录会按id顺序依次累加,结果完全正确,而且效率比子查询高很多。
总结
- 子查询法可以通过添加唯一排序字段修复日期相同的错误,但效率确实不如变量法;
- 变量法不仅性能更优,写法也更简洁,只要保证先排序就能得到正确结果。
内容的提问来源于stack exchange,提问作者colin
相关产品推荐
相关产品推荐

