MySQL数列求和

问题

2016年年终时,呵呵保险公司要对其销售人员的业绩做汇总和评估,以便为员工发放年终奖。而每个销售人员分属不同小组,每个小组由各自组长管理。每个小组分属不同部门,每个部门有部门经理。部门经理向总监汇报工作,总监又要向总裁、副总裁等汇报工作。所以,在统计每个销售人员时,公司希望同时对组长、经理、总监等业绩做统计。除销售人员外,其它职位的业绩等于其直属下的业绩之和,再加上自身的业绩。

比如,下表展示了一份简单的年度业绩表,每条记录描述了某位员工的年度业绩,name是员工的姓名,position是其所属职位,office代表其所属单位,performance即业绩数额。比如Tom属于保险部门的财产险推广小组,Jerry属于保险部门的事故险服务小组。

name position office performance
Tom salesman insurance_belongings_promotion $600
Jerry salesman insurance_accident_service $300
Bob leader insurance_belongings $1200
Alice leader insurance_accident $800
Susan manager insurance $2000

由上表的销售业绩,公司希望得到对其统计汇总后的结果,正确的结果如下表

name performance
Tom $600 (= Tom)
Jerry $300 (= Jerry)
Bob $1800 (= Tom + Bob)
Alice $1100 (= Jerry + Alice)
Susan $4900 (= Bob + Alice + Susan)

由于呵呵公司一直倡导人人平等的理念,所以在年终奖分配上,虽然奖金数额是由按照员工业绩的多少来决定,但公司不希望这件事情被大家所知。因此,公司的另一个需求是,尽可能使用少的资源和工具,来对业绩做统计,以来减少大家对此事的关注度。在和呵呵公司的技术人员沟通后,决定仅使用SQL语言。也就是说,上述所有逻辑的实现,不能借助例如Python、Java等代码,只能够使用数据库(MySQL 5.7)支持的SQL语言。

解决方案

如果公司仅仅是统计每个单位的自身业绩和,那么只要 GROUP BY office就可以了。然而这个问题,更多的是按照office的规则,统计自身及所有下级单位的业绩之和。

首先,通过观察样例,可以发现position字段是没有用的,可以直接剔除掉。那么会转化成下表

name office performance
Tom insurance_belongings_promotion $600
Jerry insurance_accident_service $300
Bob insurance_belongings $1200
Alice insurance_accident $800
Susan insurance $2000

然后,由于office的字段规则不直观,所以再对office列进行改写和抽象。首先考虑一个更为简单的模型,如下表

name office performance
Tom 1 $600
Bob 2 $1200
Susan 3 $2000

假设现在的需求依旧是统计不同office的业绩。而每个office的业绩,即为自身的业绩加上所有比自己数值小的office的业绩。我们可以证明,简化后的模型,与原问题没有本质区别,只不过对office的规则逻辑进行了简化,使用int型的大小来表明所属关系。简化模型的好处是,可以省掉不必要的精力,使我们更关注问题最需要解决的部分。

通过对模型的简化,我们可以发现,这个问题被转化为一个数列求和的问题。其中,office代表了某条记录在数列A中的位置,我们想要得到数列任意位置的前序和SUM。例如

  • 数列

    • A[1] = {Tom, 600}
    • A[2] = {Bob, 1200}
    • A[3] = {Susan, 2000}
  • 前序和

    • SUM[1] = {600}

    • SUM[2] = {1800}

    • SUM[3] = {4400}

除去无关的文本属性name,我们再次对模型进行抽象,假设有下表A,代表数列A,包含key和value

key value
1 4
2 1
3 6

我们希望得到下表SUM,代表前序和SUM,包含key和sum

key sum
1 4
2 5
3 11

一个很容易想到的方法是通过分组,将每个(key, value)划分到其参与求和的组中,如(1, 4)仅会分配到第1组,(2, 1)会分配到第1和第2组。但是,通过GROUP BY操作,仅仅使用一张A表,是没有办法完成上述分组。

那么,能不能通过自身JOIN的方法,来完成上述的分组呢,答案是肯定的。通过A表与自身的JOIN,得到下表

a0_key a1_key a1_value
1 1 4
2 1 4
2 2 1
3 1 4
3 2 1
3 3 6

SQL语句呼之欲出,如下

SELECT a0.key as a0_key, a1.key as a1_key, a1.value as a1_value
FROM a as a0, (SELECT * FROM a) as a1
WHERE a0.key >= a1.key 

而后,只要按a0_key分组就可以得出前序和

SELECT a0_key as key, SUM(a1_value) as sum
FROM a0_a1

到此,MySQL求前序和的问题被完美解决。

后记

这种解决方案,由于用到了join操作,所以在性能和空间消耗上,或许不是特别理想。通过分析发现,若原表有N条记录,那么运行中需要占用的空间复杂度为O(N2),那么,假设原表有10条记录,在join时

a0 a1 a0_a1
10 10 O(10 x 10 = 100)

同样的,假设原表有100MB记录,那么在join时

a0 a1 a0_a1
100MB 100MB O(100 x 100 = 10000MB)

不过,这里存在一个优化方法。我们可以设定数据的度量单位为GB,这样,在join时,就会占用更小的内存空间,如下

a0 a1 a0_a1
100MB ≈ 0.1GB 100MB ≈ 0.1GB O(0.1 x 0.1 = 0.01GB ≈ 10MB)

所以,通过单位的转化,可以极大的缩小中间计算的空间资源使用,这也符合呵呵公司的最初需求。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,064评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,606评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,011评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,550评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,465评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,919评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,428评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,075评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,208评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,185评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,191评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,914评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,482评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,585评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,825评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,194评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,703评论 2 339

推荐阅读更多精彩内容