MongoDB Shell 基本命令(三)聚合管道
-
管道含义
类似Linux中的管道,前一个命令的输出作为后一个命令的输入。
-
显示网络连接、路由表和网络接口统计信息
netstat -ano
-netstat:network statistics 网络统计
-a:显示所有连接和监听端口,包括所有活动的TCP和UDP连接。
-n:以数字形式显示地址和端口号,而不是尝试解析为主机名和服务名称。
-o:显示与每个连接关联的进程ID(PID),这使得用户可以知道哪个进程正在使用特定的网络连接。
加上管道,查看与某个特定端口(27017)相关的连接,findstr相当于Linux中的grep,用于查找字符串的命令行工具。
netstat -ano | findstr :27017
“ESTABLISHED” 是 TCP 协议的一种状态,表示一条 TCP连接已经成功建立,并且可以开始数据通信。
加上管道,只查看LISTENING相关连接
netstat -ano | findstr :27017 | findstr :27017 | findstr LISTENING
所以,管道的含义就是:前一个命令的结果,作为后续命令的输入。我们可以在前面查询的基础上,整理过后,再进行查询。
select count(*) from tabname where ...;
select sum() from tabname group by ...;先分组再统计,相当于是多步操作
- count()和distinct()
use cqust;
//如果查询条件为空,查询文档记录数
db.students.count();//集合的文档总数
//统计女生/男生的数量
var 查询条件={gender:0};
db.students.count(查询条件);
//第二种写法
db.students.find(查询条件).count()//count(查询条件)等价于find(查询条件).count()
//select distinct major from tbname
//distinct,获取不重复的取值
//查询2022级有哪些专业
var 查询条件={grade:2022};
var 返回字段={_id:0,major:1,grde:1};
db.students.find(查询条件,返回字段);//有重复值
//格式:db.students.distinct(取值字段,查询条件)
db.students.distinct("major",查询条件);//返回数组
//2022级学生上了哪些课程
db.students.distinct("courses.course",{grade:2022});
//一共有哪些课程
db.students.distinct("courses.course");
- 聚合管道
- 聚合是数据处理和分析的过程,对来自多个文档的数据进行计算、总结和分析,强调数据的汇总和计算。
- 管道是一个结构,通过多个处理阶段(每个阶段可以执行不同类型的操作)来实现聚合的过程。
- 分组操作用到聚合管道。
- $match:过滤输入文档,只有满足指定条件的文档会被传递到管道的下一个阶段。where(sql)
- $group:将输入文档分组,并按指定的键对每个组编制汇总。可以计算总和、平均值等。
- $sort:对输入文档进行排序。1、0升序和降序;
- $project:重塑输入文档,选择需要的字段并可以添加计算字段。(字段名修改、增加字段)
- limit和skip:用于限制返回的结果数量或跳过指定数量的文档。
第一阶段:$match
阶段按status
字段过滤文档,并将status
等于"A"
的文档传递到下一阶段。
第二阶段:$group
阶段按cust_id
字段将文档分组,以计算每个cust_id
唯一值的金额总和。
$match
的含义- 功能: $match
阶段用于过滤集合中的文档,以保留所有符合指定条件的文档。它的作用类似于 SQL 中的 WHERE
子句。
- 聚合管道格式
db.orders.aggregate([
{ $match: { status: "A" } },
{ $group: { _id: "$cust_id", total: { $sum: "$amount" } } }
])
//var 步骤1={步骤操作符:操作文档}
//var 步骤数组=[步骤1,步骤2,步骤3,...]
//db.集合名.aggregate(步骤数组)
{$group:{_id:分组字段,聚合字段:{$统计操作:$值字段}}}
{$project:{field1:1,newfield2:$oldfield2}}
{$sort:{field1:1,field2:-1}}
{$limit},{$skip}
{$unwind:"$listfield"} 将数组中的元素分配到多行(拉平数组)
- 筛选出选择分布式数据库原理与应用的学生信息
var 步骤1 = {$match:{"courses.course":"分布式数据库原理与应用"}};
var 步骤1 = {$match:{"courses.course":/分布式/}};//正则表达式,匹配包含”分布式“
var 步骤数组 = [步骤1];
db.students.aggregate(步骤数组);
- 统计男女生人数 0女生 1男生
//select count(*) as 人数,gender from students group by gender
//group操作符,取值进行group by
var 步骤1 = {$group:{_id:"$gender",人数:{$sum:1}}};
var 步骤数组 = [步骤1];
db.students.aggregate(步骤数组);
- 为了让刚才的结果更直观,用到project ,这个阶段在数据处理过程中起到了选择、重命名和计算字段的重要作用。
var 步骤1 = {$group:{_id:"$gender",人数:{$sum:1}}};
var 步骤2 = {$project:{性别:"$_id",人数:1,_id:0}};//1表示返回原来的人数
var 步骤数组 = [步骤1,步骤2];
db.students.aggregate(步骤数组);
- 统计大数据专业男女生人数
var 步骤0={$match:{"major":"大数据"}};
var 步骤1={$group:{_id:"$gender",人数:{$sum:1}}}//$表示值,sum:1相当于是count
var 步骤2={$project:{性别:"$_id",人数:1,_id:0}}
var 步骤数组=[步骤0,步骤1,步骤2]
db.students.aggregate(步骤数组)
- 统计2022级大数据专业1班男女生人数
//非分组做法,分别统计
db.students.count({major:"大数据",grade:2022,class:1,gender:0});//求出女生人数
db.students.count({major:"大数据",grade:2022,class:1,gender:1});//求出男生人数
//分组做法
var 先找出本班所有学生的记录 ={$match:{major:"大数据",grade:2022,class:1}};
var 计算男女生的数量={$group:{_id:"$gender",人数:{$sum:1}}};//相当于是求了count
db.students.aggregate([先找出本班所有学生的记录,计算男女生的数量]);
- 统计2022级大数据专业1班男女体重和
//db.students.aggregate([{阶段1},{阶段2},{阶段3},...])
//db.students.aggregate([先找出本班所有学生的记录,然后分男女计算体重的和,整理结果]);
//统计2022级大数据专业1班男女体重和
//select sum(bodyweight) as 总体重, gender as 性别
//from tbstudents
//group by gender
//where major="大数据" and grade=2020 and class=1
var 先找出本班所有学生的记录 = {$match:{major:"大数据",grade:2022,class:1}};
var 然后分男女计算体重的和 = {$group:{_id:"$gender",总体重:{$sum:"$body.weight"}}};
var 整理结果 = {$project:{_id:0,"性别":"$_id",总体重:"$总体重","单位":"kg"}};
db.students.aggregate([先找出本班所有学生的记录,然后分男女计算体重的和,整理结果])
- 加减乘除运算符号 a d d 、 add、 add、substract、 m u l t i p l y 、 multiply、 multiply、divide
var 先找出本班所有学生的记录 = {$match:{major:"大数据",grade:2022,class:1}};
//var 然后分男女计算体重的和 ={$group:{_id:分组字段,聚合字段:{$统计操作:$值字段}};
var 然后分男女计算体重的和 = {$group:{_id:"$gender",总体重:{$sum:"$body.weight"}}};
//var 整理结果 = {$project:{_id:0,"性别":"$_id",总体重:"$总体重","单位":"kg"}};
//var 整理结果 = {$project:{_id:0,"性别":"$_id",总体重:{$add:["$总体重",1000]},"单位":"kg"}};//通过$add增加1000
//var 整理结果 = {$project:{_id:0,"性别":"$_id",总体重:{$subtract:["$总体重",2000]},"单位":"kg"}};//通过$add减少2000
var 整理结果 = {$project:{_id:0,"性别":"$_id",总体重:{$multiply:["$总体重",2]},"单位":"kg"}};//通过$add减少2000
//var 整理结果 = {$project:{_id:0,"性别":"$_id",总体重:{$divide:["$总体重",1000]},"单位":"t"}};//通过$divide将体重从kg转换为吨
db.students.aggregate([先找出本班所有学生的记录,然后分男女计算体重的和,整理结果]);
- 统计各科课程的平均分
// "courses": [
// {
// "course": "结构化数据存储与应用",
// "credit": 2,
// "score": 67
// }
// ]
//var 分组统计 = {$group:{_id:分组字段,聚合字段:{$统计操作:$值字段}}}
var 分组统计 = {$group:{_id:"$courses.course",平均分:{$avg:"$courses.score"}}};
db.students.aggregate([分组统计]);//直接这样查询有问题,对于数组\数组字段,无法直接分组统计
二维------>一维(unwind) ;
{ u n w i n d : " unwind:" unwind:"listfield"} 将列表中的元素分配到多行(拉平数组)
正确写法
//需要先将数组拉平,也就是说将数组中的值拆出来与其他字段连接
// {A,B:[1,2]} => {A,B:1} {A,B:2}
var 平摊数组 = {$unwind:"$courses"};
var 分组统计 = {$group:{_id:"$courses.course", 平均分:{$avg:"$courses.score"}}};
//db.students.aggregate([平摊数组]);//此时可以看出,数组已经被拉开了
db.students.aggregate([平摊数组,分组统计]);
- 在上一步基础上,利用 s o r t 和 sort和 sort和limit返回前3结果
var 平摊数组 = {$unwind:"$courses"};
var 分组统计 = {$group:{_id:"$courses.course", 平均分:{$avg:"$courses.score"}}};
var 排序 = {$sort:{平均分:-1}};
var 返回前3结果 = { $limit : 3 }
db.students.aggregate([平摊数组,分组统计,排序,返回前3结果]);
- 在上一步基础上,统计平均分大于74.5分的课程
//select avg(score) 平均分,course from tbstudents group by course having 平均分>=74.5
var 平摊数组 = {$unwind:"$courses"};
var 分组统计 = {$group:{_id:"$courses.course", 平均分:{$avg:"$courses.score"}}};
var 结果筛选 = {$match:{"平均分":{$gte:74.5}}}
db.students.aggregate([平摊数组,分组统计,结果筛选])// 方法用于在集合上执行一系列的聚合操作。
- 统计分布式数据库原理与应用的平均分
var 平摊数组 = {$unwind:"$courses"};
var 分组统计 = {$group:{_id:"$courses.course", 平均分:{$avg:"$courses.score"}}};
var 结果筛选 = {$match:{"_id":/分布式/}}//非courses.course中,$match 是一个筛选操作符,用于过滤文档
//使用了正则表达式来匹配 _id 字段包含“分布式”字符串的文档。
var 整理结果 = {$project:{"course":"$_id","avgscore":{$round:["$平均分",2]},_id:0}}//$project 用于选择和重命名文档中的字段。指定将 _id 字段重命名为 course,平均分 字段重命名为 avgscore,并且排除 _id 字段在输出结果中显示(_id: 0 表示不显示 _id 字段)。{$round:["$平均分",2]}四舍五入,保留两位小数
db.students.aggregate([平摊数组,分组统计,结果筛选,整理结果])
- 统计分布式数据库课程的平均分,要求保留两位小数
//统计分布式数据库课程的平均分,要求保留两位小数
var 平摊数组 = {$unwind:"$courses"};
var 分组统计 = {$group:{_id:"$courses.course", 平均分:{$avg:"$courses.score"}}};
var 整理结果 = {$project:{"course":"$_id","avgscore":{$round:["$平均分",2]},_id:0}}//$project 用于选择和重命名文档中的字段。指定将 _id 字段重命名为 course,平均分 字段重命名为 avgscore,并且排除 _id 字段在输出结果中显示(_id: 0 表示不显示 _id 字段)。{$round:["$平均分",2]}四舍五入,保留两位小数
db.students.aggregate([平摊数组,分组统计,结果筛选,整理结果])
- 统计各班级(专业\年级\班级)分布式数据库课程的平均分
//方法一:直接连接
var 拉平数组 = {$unwind:"$courses"};
var 查找课程 = {$match:{"courses.course":/分布式/}};
//_id是一个组合字段,包括专业、年级、班级信息
var 按班级统计平均分 = {$group:{_id:{专业:"$major",年级:"$grade",班级:"$class"},平均分:{$avg:"$courses.score"}}}
var 整理结果 = {$project:{专业:"$_id.专业",年级:"$_id.年级",班级:"$_id.班级",平均分:{$round:["$平均分",2]},_id:0}};
db.students.aggregate([拉平数组,查找课程,按班级统计平均分,整理结果]);
//方法二:利用$concat连接,$tostring强制转换
var 拉平数组 = {$unwind:"$courses"};
var 查找课程 = {$match:{"courses.course":/分布式/}};
//_id是一个组合字段,包括专业、年级、班级信息
var 按班级统计平均分 = {$group:{_id:{"行政班级":{$concat:["$major",{$toString:"$grade"},"-0",{$toString:"$class"}]}},平均分:{$avg:"$courses.score"}}};
var 整理结果 = {$project:{"行政班级":"$_id.行政班级",平均分:{$round:["$平均分",2]},_id:0}};
var 排序 = {$sort:{"行政班级":1}};//按照行政班级升序
db.students.aggregate([拉平数组,查找课程,按班级统计平均分,整理结果,排序]);
- 按专业求女生平均身高低于170的学生,并排序($sort)
/*
SQL: select AVG(height) as avgHeight, major as _id
from students
where gender=0
group by major
having avgHeight<=170
sorted by avgHeight DESC
*/
db.getCollection("students").aggregate([
//第一步,查数据
{$match:{gender:0}}
//第二步,限制返回字段
,{$project:{_id:0,major:1, grade:1, class:1, height:"$body.height"}}
//第三步,分组求平均值
// _id: group by的字段, 字段名要加$符号前缀,表示是一个字段名
// avgHeight: 新生成的平均值字段名
// $avg: 平均值操作符,它的值为要求平均值的字段名,注意加$前缀
,{$group:{_id:"$major", avgHeight:{$avg:"$height"}}}
//第四步,筛选聚合结果
,{$match:{avgHeight:{$lte:170}}}
//第五步,排序,按照avgHeight的值从大到小排序, -1: DESC, 1:ASC
,{$sort:{avgHeight:-1}}
//,{$limit:1} //取结果中最接近170的值
])
- 课堂练习
1.统计各课程的平均分,并返回倒数第一名的结果
2.统计本专业分布式数据库课程的平均分,并且保留两位小数