MongoDB 分组统计
MongoDB 作为 NoSql 文档型数据库,在全球范围得到广泛的支持与应用。在比较常用的数据库功能中,相对于普通的增删改查,使用 group 聚合分组统计有些复杂,而 MongoDB 也给予了支持。本文将对MongoDb分组的实现方法及示例进行分析,通过在 MongoDB 脚本中操作、使用集算器 SPL 语言操作两种操作途径,进行简单的归纳总结。具体的问题场景包括以下几个方面:
- 内嵌数组结构的统计........................................................................... 1
- 内嵌文档求和..................................................................................... 2
- 分段分组结构统计.............................................................................. 4
- 多字段分组统计................................................................................. 6
- 内嵌数组结构的统计
对嵌套数组结构中的数据进行统计处理例如查询考试科目的平均分及每个学生的总成绩:
测试数据:
由于各科分数 scroe 是按课目、成绩记录的数组结构,统计前需要将它拆解,将每科成绩与学生对应,然后再实现分组计算。这需要熟悉 unwind 与 group 组合的应用。
脚本说明:
A1:连接 mongodb 数据库。
A2:获取 student 表中的数据。
A3:将 scroe 数据合并成序表,再按课程分组,计算平均分。
A4:统计每个学生的成绩后返回列名为 NAME、TOTAL 的序表。new 函数表示生成新序表。
A5:关闭数据库连接。
这个嵌套结构统计的例子比较常见,相信很多人都遇到过,需要先拆解再分组计算,主要是熟悉 mongodb 对嵌套数据结构的处理。
- 内嵌文档求和
对内嵌文档中的数据求和处理, 例如统计下面每条记录中 income,output 的数量和。
测试数据:
Mongodb脚本:
var fields = [ "income", "output"];
db.computer.aggregate([
{
$project:{
"values":{
$filter:{
input:{
"$objectToArray":"$$ROOT"
},
cond:{
$in:[
"$$this.k",
fields
]
}
}
}
}
},
{
$unwind:"$values"
},
{
$project:{
key:"$values.k",
values:{
"$sum":{
"$let":{
"vars":{
"item":{
"$objectToArray":"$values.v"
}
},
"in":"$$item.v"
}
}
}
}
},
{$sort: {"_id":-1}},
{ "$group": {
"_id": "$_id",
'income':{"$first": "$values"},
"output":{"$last": "$values"}
}},
]);
oe 是按课目、成绩记录的数组结构,统计前需要将它拆解,将每科成绩与学生对应,然后再实现分组计算。这需要熟悉 unwind 与 group 组合的应用。
SPL 脚本 (student.dfx):
filter将income,output 部分信息存放到数组中,用 unwind 拆解成记录,再累计各项值求和,按 _id 分组合并数据。
SPL脚本:
脚本说明:
A1:连接数据库
A2:获取 computer 表中的数据
A3:将 income、output 字段中的数据分别转换成序列求和,再与 ID 组合生成新序表
A4:关闭数据库连接。
获取子记录的字段值,然后求和,相对于 mongo 脚本简化了不少。这个内嵌文档与内嵌数组在组织结构上有点类似,不小心容易混淆,因此需要特别注意与上例中的 scroe 数组结构比较,写出的脚本有所不同。
- 分段分组结构统计
统计各段内的记录数量。例如下面按销售量分段,统计各段内的数据量,数据如下:
Mongo 脚本
var a_count=0;
var b_count=0;
var c_count=0;
var d_count=0;
var e_count=0;
db.sales.find({
}).forEach(
function(myDoc) {
if (myDoc.SALES <3000) {
a_count += 1;
}
else if (myDoc.SALES <5000) {
b_count += 1;
}
else if (myDoc.SALES <7500) {
c_count += 1;
}
else if (myDoc.SALES <10000) {
d_count += 1;
}
else {
e_count += 1;
}
}
);
print("a_count="+a_count)
print("b_count="+b_count)
print("c_count="+c_count)
print("d_count="+d_count)
print("e_count="+e_count)
这个需求按条件分段分组,mongodb 没有提供对应的 api,实现起来有点繁琐,上面的程序是其中实现的一个例子参考,当然也可以写成其它实现形式。下面看看集算器脚本的实现。
SPL脚本:
脚本说明:
A1:定义 SALES 分组区间。
A2:连接 mongodb 数据库。
A3:获取 sales 表中的数据。
A4:根据 SALES 区间分组统计员工数。其中函数 pseg()表示返回成员在序列中的区段序号,int() 表示转换成整数。
A5:关闭数据库连接。
Mongodb脚本与 SPL 脚本都实现了预期的结果,但函数pseg 的使用让 SPL 脚本精简了不少。
- 多字段分组统计
统计分类项下的总数及各子项数。下面统计按 addr 分类的 book 的数量以及其下不同 book 类型的数量。
Mongo脚本
db.books.aggregate([
{ "$group": {
"_id": {
"addr": "$addr",
"book": "$book"
},
"bookCount": {"$sum": 1}
}},
{ "$group": {
"_id": "$_id.addr",
"books": {
"$push": {
"book": "$_id.book",
"count": "$bookCount"
},
},
"count": {"$sum": "$bookCount"}
}},
{"$sort": { "count": -1} },
{ "$project": {
"books": {"$slice": [ "$books", 2] },
"count": 1
}}
]).pretty()
先按 addr,book 分组统计 book 数,再按 addr 分组统计 book 数,调整显示顺序。
SPL脚本 (books.dfx):
脚本说明:
A1:连接 mongodb 数据库。
A2:获取 books 表中的数据。
A3:按 addr,book 分组统计 book 数顾。
A4:再按 addr 分组统计 book 数。
A5:将 A4 中的 Total 按 addr 关联后合并到序表中。
B5: 返回序表 A5。
A6:关闭数据库连接。
这个例子中的 SPL 脚本除了一如既往的精简清晰外,还显示了如何简单方便地与 Java 程序集成。
在 Java 程序中如果要对 MongoDB 实现上面的分组统计功能,需要根据不同的需求重新一五一十地实现,比较麻烦的同时也不通用。而如果用集算器来实现就容易多了,集算器提供了 JDBC 驱动程序,支持在 Java 程序中用 JDBC 存储过程方式访问计算结果,调用方法与调用存储过程相同。(JDBC 具体配置参考《集算器教程》中的“JDBC基本使用”章节)
Java 调用主要过程如下:
public void testStudent (){
Connection con = null;
com.esproc.jdbc.InternalCStatement st;
try{
// 建立连接
Class.forName("com.esproc.jdbc.InternalDriver");
con= DriverManager.getConnection("jdbc:esproc:local://");
//调用存储过程,其中books是 dfx 的文件名
st =(com. esproc.jdbc.InternalCStatement)con.prepareCall("call books ()");
//执行存储过程
st.execute();
// 获取结果集
ResultSet rs = st.getResultSet();
。。。。。。。
catch(Exception e){
System.out.println(e);
}
可以看到,集算器的计算结果能够很方便地供 Java 应用程序使用。除了上面的调用方式,程序也可以修改成直接加载 SPL 脚本的函数,用 SPL 脚本文件名当参数来实现。同时,集算器也支持 ODBC 驱动,与其它支持 ODBC 的语言集成也与此类似。
简单总结一下,MongoDB 的聚合分组计算的操作与存储文档的结构息息相关,丰富的文档结构一方面有利于存储,同时数据查询展示也可以做到多样化,但另一方面也带来了 shell 脚本操作的复杂性,写起来比较不容易, 需要考虑的细节、步骤也比较多。通过上面这几个简单案例的分析比较,可以看到集算器 SPL 在实现分组统计方面能简化操作,降低难度,从而有效地帮助我们解决问题。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Java 并发工具包 | J.U.C
Java 并发工具包 | J.U.C 不知道大家还有没有印象,上次我们已经说过了,我们为了实现集合相关类的线程安全,JDK 提供了一套同步容器,也就是 Vector,Hashtable,还有一个 Collections 工具类中的几个方法。 问题是什么呢,同步容器并不能保证线程安全,我在们写代码的时候还需要注意一些方法的使用,在 JDK 1.5 及以后就出现了 java.util.current 包,这个包中就提供了大量的类来实现线程安全,这也就是我们经常说的 JUC。 举例例子吧,与 HashMap 对应的线程安全的容器,ConcurrentHashMap 就是出自这个包。 稍微整理了一下这个包中都包含了哪些功能,做个思维导图。 不得不说,这其中的知识点非常多,我这只是列出了一部分,作为大纲,我呢也不可能都说,用到的类也很少,目前工作基本接触不到这些类的使用。 但是吧,我还是强烈建议有基础的同学看看源码,膜拜一下大神。 我就简单说几个面试常问的,并发容器,atomic 包,Lock 和其实现类,线程池。 并发容器最最常问的就是 ConcurrentHashMap 的原...
- 下一篇
这里有20位程序员关于求职的疑惑,还好也有参考答案
阅读本文大概需要 6 分钟。 前几天发了一条朋友圈对于求职小伙伴们提出的问题,我进行了收集整理,统一反馈。也许这20个问题也是你们遇到的问题,所以趁着年前赶紧把它发出来。 以下20个问题基本上都是读者的原话,当然我稍微修改了一些不通顺的地方。 0.我觉得最大的疑惑就是自己目前的水平和合各大公司的要求之间的符合度。以前没有实习过,所以还是有点虚的,再个就是总是听说每次面试都会有记录,可能会影响后面的面试。所以要准备到什么程度才可以投春招提前批或者春招面试呢?是海投还是投几个比较有意向的呢?很多帮内推的都会说面试不好也不会影响后面的面试,不知真假? 建议先去小公司试水,如果问题不大,可以尝试投大公司。另外记得刷一下大厂的面经,如果觉得差距比较大,慎投,至少等你把面经商遇到的问题都解决了再继续下一步吧。 面试可能会有记录,但是这个不用太过关注,最重要的是要确定自己复习到位。 1.不知道达到什么水平能面试通过 这个问题其实不太明确,但是也很好回答。首先确定好你的意向岗位,再按照公司岗位要求里的技能要求进行复习,同时你也要去看看该公司这个岗位的面经。 举个例子,这是阿里的招聘要求。 看起来有很多...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- 2048小游戏-低调大师作品
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Hadoop3单机部署,实现最简伪集群