前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MongoDB 挑战传统数据库聚合查询,干不死他们的

MongoDB 挑战传统数据库聚合查询,干不死他们的

作者头像
AustinDatabases
发布2024-05-10 17:35:36
890
发布2024-05-10 17:35:36
举报
文章被收录于专栏:AustinDatabasesAustinDatabases

说句不怕笑话的话,MongoDB使用也有6 7 8 年了,但对于聚合一般我是抗拒的,可能是MOGNODB 3.X落下的顽疾,一听到用MongoDB 做聚合操作,一般都不想听 不想听。但时代不一样,MongoDB已经走到了 8.0UP,聚合早就和之前不一样了。

所以怕也的上,还的学习。 以上学习基于MOGNODB7.0 ,聚合操作中首选的方案是聚合管道,或者使用单一聚合的方法。一般来说聚合操作中的管道操作,主要是通过多个阶段来处理数据,比如第一需要先过滤数据,然后对过滤的数据进行文档的分组并计算聚合操作后的结果。同时聚合还可以进行聚合后的数据更新,当然这需要在4.2后的版本才有此功能。

我们先产生测试数据,先简单产生 4万条数据

代码语言:javascript
复制
mongo7 [direct: primary] test> function insertData(dbName, colName, num) {
... 
...   var col = db.getSiblingDB(dbName).getCollection(colName);
... 
...   for (i = 0; i < num; i++) {
...     col.insert({x:i});
...   }
... 
...   print(col.count());
... 
... }
[Function: insertData]
mongo7 [direct: primary] test> insertData("test", "testData", 40000)
DeprecationWarning: Collection.insert() is deprecated. Use insertOne, insertMany, or bulkWrite.
DeprecationWarning: Collection.count() is deprecated. Use countDocuments or estimatedDocumentCount.
40000


下面我们开始,假设一个需求,我需要计算插入数据中 10000 到 20000 ,20000 到 30000 之间的数据值的累加和并展示出来。

代码语言:javascript
复制
mongo7 [direct: primary] test> db.testData.aggregate([
...   {
...     $match: { 
...       x: { $gte: 10000, $lt: 30000 }
...     }
...   },
...   {
...     $group: {
...       _id: null,
...       sum1: { $sum: { $cond: [{ $and: [ { $gte: ["$x", 10000] }, { $lt: ["$x", 20000] } ] }, "$x", 0] } },
...       sum2: { $sum: { $cond: [{ $and: [ { $gte: ["$x", 20000] }, { $lt: ["$x", 30000] } ] }, "$x", 0] } }
...     }
...   }
... ])
[ { _id: null, sum1: 149995000, sum2: 249995000 } ]
mongo7 [direct: primary] test> 


上面的语句,直接将结果进行了计算和展示非常快。

首先这边语句分为两个部分,第一部分是限制数据进入,因为这里计算是大于等于10000 和 小于30000,所以我们需要根据match 来进行数据的排除,将9999以内的数字和30000以外的数据进行,排除。

 $match: { x: { $gte: 10000, $lt: 30000}

然后留下的是我们的要处理的数据,进行数据的聚合操作。

 $group: {

      _id: null,

      sum1: { $sum: { $cond: [{ $and: [ { $gte: ["$x", 10000] }, { $lt: ["$x", 20000] } ] }, "$x", 0] } },

      sum2: { $sum: { $cond: [{ $and  [ { $gte: ["$x", 20000] }, { $lt: ["$x", 30000] } } }, "$x", 0] } }

这里在过滤出我们要的数据后,首先我们遇到的是针对什么进行分组,如果是传统数据库,这里面一般就头疼了,分组是没有字段的,这里MongoDB 是可以针对没有分组的聚合数据进行分组的,上面就是一个案例,我们只有object_id  , x 两个字段,我们怎么聚合分组我们的分组实际是值,这也是传统DBA 烧脑的开始。

我们这里根据过滤出的条件,分别对于符合条件的数据进行聚合。

 sum1: { $sum: { $cond: [{ $and: [ { $gte: ["$x", 10000] }, { $lt: ["$x", 20000] } ] }, "$x", 0] } }

这条的意思是,首先要表达的是我们要进行 sum的操作,也就是累加和,然后 cond 的意思是在我们match后的数据还需要进行条件的筛选,也就是我这里只要大于等于10000 和小于20000的数,进行累加和,如果这里条件都不符合的话,我们就给一个默认的值 0 

代码语言:javascript
复制
mongo7 [direct: primary] test> db.testData.aggregate([
...   {
...     $match: { 
...       x: { $gte: 10000, $lt: 30000 }
...     }
...   },
...   {
...     $group: {
...       _id: null,
...       sum1: { $sum: { $cond: [{ $and: [ { $gte: ["$x", 10000] }, { $lt: ["$x", 20000] } ] }, "$x", 0] } },
...       sum2: { $sum: { $cond: [{ $and: [ { $gte: ["$x", 20000] }, { $lt: ["$x", 30000] } ] }, "$x", 0] } },
...       sum3: { $sum: { $cond: [{ $and: [ { $gte: ["$x", 40000] }, { $lt: ["$x", 50000] } ] }, "$x", 0] } }
...     }
...   }
... ])
[ { _id: null, sum1: 149995000, sum2: 249995000, sum3: 0 } ]
mongo7 [direct: primary] test> 

上面的例子中我们可以看到,最后一句明显就是找茬的,我们在顾虑数据的时候值包含了10000 到 30000的数据,而下面是要40000到50000的数据,这里cond 条件就产生效用了,如果符合条件则打印结果,如果不符合条件,则选择后面的给定的结果进行打印,很明不符合条件的为0

那么这样的语句还有其他的写法吗,有的,例如下面的写法

代码语言:javascript
复制
mongo7 [direct: primary] test> db.testData.aggregate( [
...    {
...       $match: { x: { $gte: 10000, $lt: 30000}  }
...    },
...  {
...     $group: {
...       _id: null,
...       sum1: { $sum: { $cond: [{ $and: [ { $gte: ["$x", 10000] }, { $lt: ["$x", 20000] } ] }, "$x", 0] } },
...       sum2: { $sum: { $cond: { if: { $gte: ["$x", 20000] }, then: "$x", else: 0 } } }
...     }
...   }
... ] )
[ { _id: null, sum1: 149995000, sum2: 249995000 } ]
mongo7 [direct: primary] test> 


我们可以看到,结果是一样的,但写法的确是不同,第二个我们采用了是条件的方式来撰写的,也就是最后一个20000到3000,所以用了另一种Mongodb的语句的写法。

sum2: { $sum: { $cond: { if: { $gte: ["$x", 20000] }, then: "$x", else: 0 } } }

这个写法的意思是,如果值大于等于20000的话,那么就取值,否则就是0

明显这里是一个判断的方式的表达,如果想用SQL 来表达类似的意思可以写成,下图方式

代码语言:javascript
复制
SELECT
    NULL AS _id,
    SUM(CASE WHEN x >= 10000 AND x < 20000 THEN x ELSE 0 END) AS sum1,
    SUM(CASE WHEN x >= 20000 AND x < 30000 THEN x ELSE 0 END) AS sum2,
    SUM(CASE WHEN x >= 40000 AND x < 50000 THEN x ELSE 0 END) AS sum3
FROM
    testData
WHERE
    x >= 10000 AND x < 30000;


最后在给传统数据库DBA 来一个烧脑的作为此次的结尾,这样的数据查询如果是在传统数据库,相比是有索引也走不了,作为传统的DBA 对于这样的语句,在X列加索引,是不会抱有希望的。

这里我们也比较一下,在对X key 加索引后的查询执行计划,是否有不同,答案是当然有不同。

1 不加索引,时从执行计划看,走了全collection扫描是没跑了

2 添加索引后

结果与传统数据库的思路不一样,传统思路这样的查询这样的量是无法走索引的,全表扫描是一定的,而在NOSQL数据库中,这样的情况添加了索引也可以运行并使用,后续还的学习和发现,目前写不下去了,需要散热

后记,随着文档型数据库的被熟知,并且步步紧逼传统数据库一些事务,跨表,跨库查询,以及聚合查询等方案的退出,以及天然的分布式存储方式,和传统数据库打死都没有的灵活性,文档数据库和传统型数据库PK 的还在后面。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-05-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 AustinDatabases 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档