Rolling Sum SQL

时间:2012-08-05 05:25:59

标签: sql performance sum

我有一个非常简单的SQL要求,但是由于遇到了性能问题,我想知道下面场景的“最佳实践”是什么。

我有一个团队列表,每周/每周这些团队支付游戏费。如果团队没有支付,那么他们将有一个未结余额。所有团队付款都会进入付款表,这个表越来越大。返回具有当前余额的团队列表的最佳做法是什么?

我现在拥有的内容:

Select teams.*, (Select SUM(amount) from payments p where p.TeamID=teams.TeamID) as teambalance 
from (select TeamID, TeamName from Teams) teams

4 个答案:

答案 0 :(得分:2)

我已经考虑了很多,并且认为“不要两次存储相同信息”的经典建议在这里是错误的,或者至少被误解了。

考虑银行必须如何做到这一点。显然,当您想知道自己的当前余额并且您已经成为客户20年时,他们不会将20年的帐户活动加起来以找到您当前的余额。鉴于此,我认为有两种方法可以处理它:

  1. 选择要关闭的期间,并始终从最后一个关闭期间开始计算。这使得总结相对较短。月度声明可能是一个很好的锚。您是否有类似的自然时间段或业务生命周期?
  2. 通过将您的帐户历史记录锚定在目前中,向后工作。而不是从0开始并添加,从当前余额开始并返回。在我看来,这同样有效,并且还有一个额外的好处,就是当您想要修剪旧历史时,您不必做任何事情。存储当前的平衡,忘记假定的非规范化。目前的平衡与初始平衡一样是真实的经验事实,并且以这种方式锚定您的账户没有任何损害。
  3. 只要表现合适,您可以继续添加。但它可能不是最佳的。

    您当前的查询没问题,但不需要teams派生表。除非你使用MySQL,否则DBMS不需要这种“帮助” - 尽管MySQL实际上可能会受到它的伤害。

答案 1 :(得分:1)

select teamId,teamName,sum(amount)
from teams t join payments p on t.teamId = p.teamId
group by t.teamId, t.teamName

答案 2 :(得分:0)

我使用了两种方法来完成此任务 - 一种是您当前使用的方法。另一种是使用交叉申请。

我更喜欢你现在的方法 -

答案 3 :(得分:0)

这可能比在SELECT子句(或连接)中使用子查询更快:

select teams.TeamID, teams.teamName, team_balances.teambalance
  from teams
  join ( select TeamID, sum(amount) teambalance
           from payments
         group by TeamID
  ) team_balances
  on team_balances.TeamID = teams.TeamID;

这将按顺序扫描支付表一次,而不是进行N次索引扫描(每个团队一次)。

<强>反规范化

另一个选择是创建向teams表添加“outstanding_balance”列。

在付款表上创建触发器。在触发器中,分别根据TeamID和发票/付款金额增加或减少团队中的outstanding_balance列。

根据您的RDBMS,您还可以使用物化视图。这类似于触发方法,除了每个团队的余额将存储在不同的表中。