摘要:总之,通过这样的保存方式,我们能够实现当学生成绩改变时,快速简单的修改数据库记录,而又能够实时的查询出新的排名了。
当我们将考试分数录入系统时,会要对学生的分数进行一个排名,这个不困难。困难的是当学生的分数变更时,如何实时更新这些排名?
如果我们将排名保存为一个字段,那么意味着每次修改分数都会导致重新计算排名,以及更新数据库中的排名字段值。这个计算量可大可小,极端的情况下,如果一个学生的分数从第一名变成 0(比如因为作弊而成绩清零),那么所有学生的排名都有可能要改,这就导致大批量的数据库 update 操作。
这种方式显然是效率非常低的。那么这里给出一个解决办法,能够实现修改分数的同时,排名立刻得到更新,而无需大规模修改数据库记录。本文以 MongoDB 为例介绍如何保存这些分数。
假设我们有 10 个学生的考试成绩分别为:
50 50 50 60 60 60 60 80 90 100
那么我可以创建一条排名记录(假设这条记录在集合 scores 里面),内容如下:
{ key: "exam_001", scoreMap: [ {score:50, count:3}, {score:60, count:4}, {score:80, count:1}, {score:90, count:1}, {score:100, count:1} ] }
看到这里你就应该明白了,我保存的只是每个分数对应的人数。这样当我需要添加一个 80 分的成绩时,我只需将 {score:80, count:1} 改为 {score:80, count:2} 即可。
这种情况下如何获得一个学生的排名呢?首先要说明,每个学生对应的分数并没有保存在这里(你可以保存到另外的表里面,然后查出该学生的分数),这里保存的是分数的排名,而不是学生的排名。所以你查出分数后,对 scoreMap 数组中所有 score 大于该分数的元素的 count 值进行一个总和即可。
比如我想知道 85 分是第几名,从数组中可以算知大于 85 分的人数为 2,那么 85 分自然是第 3 名了。
那么在 MongoDB 里面,可以这样查询:
db.scores.aggregate([ {$match:{key:"exam_001"}}, {$unwind:"$scoreMap"}, {$match:{"scoreMap.score": {$gt: 85}}}, {$group:{_id:0,count:{$sum:"$scoreMap.count"}}} ]);
查出来的结果为:
{ "_id" : 0, "count" : 2 }
这种保存方式有一个极大的好处,就是不论有多少学生参加排名,我的记录数始终是有限的。假设这门考试总分 100 分,有 100 万学生参加排名,那么极端情况下 scoreMap 数组中也只会有 201 个元素(0, 0.5, 1, ... 99.5, 100)。也就是说,它的值空间是非常有限的。
有人会进一步提出:如果我将班级平均分(带两位小数)也加入这样的排名呢?值空间将会变成几万个,这样的排名查询起来岂不会很慢?
实际上不是这样子的。虽然两位小数使得值空间很大,但参与排名的班级非常有限,也就是说值的实际数量很小。如果是 100 个班级参与排名,不存在相同平均分的情况下, scoreMap 数组中也只有 100 个元素。所以计算排名丝毫不会慢下来。
如何更新现在我们的排名已经不再是直接保存在数据库,而是在查询的时候算出来,那么当一个学生的成绩变更时,我应该如何更新数据库记录呢?
当一个学生的成绩从 a 变为 b 时,我们需要做两个操作:
将成绩为 a 的学生数量 -1;
将成绩为 b 的学生数量 +1。
在 MongoDB 中,更新 scoreMap 数组元素的语句是这样的:
给某个分数的人数加一的时候:
db.scores.update( {key:"exam_001", scoreMap:{$elemMatch:{score:99.5}}}, {$inc:{"scoreMap.$.count":1}} )
给某个分数的人数减一的时候:
db.scores.update( {key:"exam_001", scoreMap:{$elemMatch:{score:99.5,count:{$gt:0}}}}, {$inc:{"scoreMap.$.count":-1}} )
注意减一的时候,查询条件加上了一个 count:{$gt:0} 的判断,这是为了避免人数变成负数。
总之,通过这样的保存方式,我们能够实现当学生成绩改变时,快速简单的修改数据库记录,而又能够实时的查询出新的排名了。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/18816.html
摘要:中文资料导航官网七牛镜像深入浅出系列进阶必读中文文档被误解的编写实战系列热门模块排行榜,方便找出你想要的模块多线程,真正的非阻塞浅析的类利用编写异步多线程的实例中与的区别管道拒绝服务漏洞高级编程业界新闻看如何评价他们的首次尝鲜程序员如何说服 node.js中文资料导航 Node.js HomePage Node官网七牛镜像 Infoq深入浅出Node.js系列(进阶必读) Nod...
摘要:介绍在博客爬虫爬取中国高校排名前名并写入中,我们利用来写爬虫,将中的大学排名表格爬取出来,并存入到中。本次分享将用的来实现相同的功能,并将爬取到的数据存入到数据库中。 介绍 在博客:Python爬虫——爬取中国高校排名前100名并写入MySQL中,我们利用Python来写爬虫,将http://gaokao.xdf.cn/201702/1... 中的大学排名表格爬取出来,并存入到My...
摘要:介绍在博客爬虫爬取中国高校排名前名并写入中,我们利用来写爬虫,将中的大学排名表格爬取出来,并存入到中。本次分享将用的来实现相同的功能,并将爬取到的数据存入到数据库中。 介绍 在博客:Python爬虫——爬取中国高校排名前100名并写入MySQL中,我们利用Python来写爬虫,将http://gaokao.xdf.cn/201702/1... 中的大学排名表格爬取出来,并存入到My...
阅读 2748·2021-11-19 11:30
阅读 3007·2021-11-15 11:39
阅读 1763·2021-08-03 14:03
阅读 1965·2019-08-30 14:18
阅读 2022·2019-08-30 11:16
阅读 2114·2019-08-29 17:23
阅读 2579·2019-08-28 18:06
阅读 2510·2019-08-26 12:22