当前位置:  开发笔记 > 编程语言 > 正文

如何在MongoDB中执行SQL Join等效项?

如何解决《如何在MongoDB中执行SQLJoin等效项?》经验,为你挑选了12个好方法。

如何在MongoDB中执行SQL Join等效项?

例如,假设你有两个集合(用户和评论),我想用pid = 444以及每个集合的用户信息来提取所有评论.

comments
  { uid:12345, pid:444, comment="blah" }
  { uid:12345, pid:888, comment="asdf" }
  { uid:99999, pid:444, comment="qwer" }

users
  { uid:12345, name:"john" }
  { uid:99999, name:"mia"  }

有没有办法用一个字段拉出所有评论(例如......查找({pid:444}))以及与每个评论相关的用户信息?

目前,我首先得到符合我标准的评论,然后找出该结果集中的所有uid,获取用户对象,并将它们与评论的结果合并.好像我做错了.



1> Clayton Guli..:

从Mongo 3.2开始,这个问题的答案大多不再正确.添加到聚合管道的新$ lookup运算符与左外连接基本相同:

https://docs.mongodb.org/master/reference/operator/aggregation/lookup/#pipe._S_lookup

来自文档:

{
   $lookup:
     {
       from: ,
       localField: ,
       foreignField: ,
       as: 
     }
}

当然,Mongo 不是一个关系数据库,并且开发人员正在谨慎地推荐$ lookup的特定用例,但至少从3.2开始就可以使用MongoDB进行连接.


是$ lookup性能方面更好的选择?

2> William Stei..:

官方mongodb网站上的这个页面正好解决这个问题:

http://docs.mongodb.org/ecosystem/tutorial/model-data-for-ruby-on-rails/

当我们显示故事列表时,我们需要显示发布故事的用户的姓名.如果我们使用关系数据库,我们可以在用户和商店上执行连接,并在单个查询中获取所有对象.但是MongoDB不支持连接,因此有时需要一些非规范化.在这里,这意味着缓存'username'属性.

关系纯粹主义者可能已经感到不安,好像我们违反了一些普遍的法律.但是请记住,MongoDB集合不等同于关系表; 每个都有一个独特的设计目标.规范化表提供原子的,孤立的数据块.然而,文档更接近地代表整个对象.在社交新闻网站的情况下,可以认为用户名是发布的故事所固有的.


@dudelgrincen这是从规范化和关系数据库转变的范式.NoSQL的目标是非常快速地从数据库读取和写入.使用BigData,您将拥有大量应用程序和前端服务器,数据库中的数字较少.您需要每秒进行数百万次交易.卸载数据库中的繁重工作并将其放到应用程序级别.如果需要深入分析,则运行将数据放入OLAP数据库的集成作业.无论如何,您不应该从OLTP dbs中获得许多深层查询.
@dudelgrincen我还应该说,并非每个项目或设计都适用.如果你有一些在SQL类型数据库中工作的东西,为什么要改变它?如果您无法按摩您的架构以使用noSQL,那么请不要.
如果用户在网站上有3.540个帖子,并且他确实在个人资料中更改了用户名,该怎么办?是否应使用新用户名更新每个帖子?
在NoSQL系统上,迁移和不断发展的模式也更容易管理.
@IvoPereira是的,这就是为什么应该避免以这种方式对数据建模的原因。有一篇文章介绍了相同的场景及其后果:[为什么不应该使用MongoDB](http://www.sarahmei.com/blog/2013/11/11/why-you-should-never-use- mongodb /)

3> 小智..:

我们可以使用mongodb客户端控制台将只有一行的简单函数合并/加入一个集合中的所有数据,现在我们可以执行所需的查询.下面是一个完整的例子

.-作者:

db.authors.insert([
    {
        _id: 'a1',
        name: { first: 'orlando', last: 'becerra' },
        age: 27
    },
    {
        _id: 'a2',
        name: { first: 'mayra', last: 'sanchez' },
        age: 21
    }
]);

.-分类:

db.categories.insert([
    {
        _id: 'c1',
        name: 'sci-fi'
    },
    {
        _id: 'c2',
        name: 'romance'
    }
]);

.-书籍

db.books.insert([
    {
        _id: 'b1',
        name: 'Groovy Book',
        category: 'c1',
        authors: ['a1']
    },
    {
        _id: 'b2',
        name: 'Java Book',
        category: 'c2',
        authors: ['a1','a2']
    },
]);

.-图书借阅

db.lendings.insert([
    {
        _id: 'l1',
        book: 'b1',
        date: new Date('01/01/11'),
        lendingBy: 'jose'
    },
    {
        _id: 'l2',
        book: 'b1',
        date: new Date('02/02/12'),
        lendingBy: 'maria'
    }
]);

.- 魔法:

db.books.find().forEach(
    function (newBook) {
        newBook.category = db.categories.findOne( { "_id": newBook.category } );
        newBook.lendings = db.lendings.find( { "book": newBook._id  } ).toArray();
        newBook.authors = db.authors.find( { "_id": { $in: newBook.authors }  } ).toArray();
        db.booksReloaded.insert(newBook);
    }
);

.-获取新的收集数据:

db.booksReloaded.find().pretty()

.-回应:)

{
    "_id" : "b1",
    "name" : "Groovy Book",
    "category" : {
        "_id" : "c1",
        "name" : "sci-fi"
    },
    "authors" : [
        {
            "_id" : "a1",
            "name" : {
                "first" : "orlando",
                "last" : "becerra"
            },
            "age" : 27
        }
    ],
    "lendings" : [
        {
            "_id" : "l1",
            "book" : "b1",
            "date" : ISODate("2011-01-01T00:00:00Z"),
            "lendingBy" : "jose"
        },
        {
            "_id" : "l2",
            "book" : "b1",
            "date" : ISODate("2012-02-02T00:00:00Z"),
            "lendingBy" : "maria"
        }
    ]
}
{
    "_id" : "b2",
    "name" : "Java Book",
    "category" : {
        "_id" : "c2",
        "name" : "romance"
    },
    "authors" : [
        {
            "_id" : "a1",
            "name" : {
                "first" : "orlando",
                "last" : "becerra"
            },
            "age" : 27
        },
        {
            "_id" : "a2",
            "name" : {
                "first" : "mayra",
                "last" : "sanchez"
            },
            "age" : 21
        }
    ],
    "lendings" : [ ]
}

我希望这条线可以帮到你.


只要您的数据很小,这就没问题了.它将把每本书内容带到您的客户端,然后逐个获取每个类别,贷款和作者.你的书成千上万的那一刻,这真的很慢.更好的技术可能是使用聚合管道并将合并的数据输出到单独的集合中.让我再次回到它.我会补充一点.
当其中一个引用对象获得更新时会发生什么?该更新是否自动反映在书籍对象中?或者这个循环需要再次运行?
我想知道是否可以使用doctrine mongodb运行相同的代码?

4> Otto Allmend..:

你必须按照你描述的方式去做.MongoDB是一个非关系型数据库,不支持连接.


从sql server背景看起来似乎是错误的性能,但它对文档db可能并不那么糟糕?
从sql server背景来看,我也很欣赏MongoDB一次性将"结果集"(带有选定的返回字段)作为新查询的输入,就像SQL中的嵌套查询一样
从mongo 3.2版本支持左连接.

5> antitoxic..:

这是一个"加入"*演员电影集合的例子:

https://github.com/mongodb/cookbook/blob/master/content/patterns/pivot.txt

它利用了.mapReduce()方法

*join - 加入面向文档的数据库的替代方案


-1,这不是从两个集合中加入数据.它使用来自单个集合(演员)的数据来旋转数据.因此,关键的东西现在是值,值现在是键......**非常不同于JOIN.
这正是你必须要做的,MongoDB不是关系型的,而是面向文档的.MapReduce允许使用具有高性能的数据(您可以使用群集等....)但即使对于简单的情况,它也非常有用!

6> grepit..:

正如其他人已经指出你试图从无关系数据库创建一个关系数据库,你真的不想这样做,但是如果你有一个案例你必须这样做,这是一个你可以使用的解决方案.我们首先在集合A(或在你的情况下是用户)中进行foreach查找然后我们将每个项目作为对象然后我们使用对象属性(在你的情况下为uid)来查找我们的第二个集合(在你的案例评论中)如果我们可以找到它然后我们有一个匹配,我们可以打印或做一些事情.希望这能帮助你,祝你好运:)

db.users.find().forEach(
function (object) {
    var commonInBoth=db.comments.findOne({ "uid": object.uid} );
    if (commonInBoth != null) {
        printjson(commonInBoth) ;
        printjson(object) ;
    }else {
        // did not match so we don't care in this case
    }
});



7> Shaurabh Bha..:

通过$ lookup,$ project$ match的正确组合,您可以在多个参数上加入多个表.这是因为它们可以链接多次.

假设我们想做以下(参考)

SELECT S.* FROM LeftTable S
LEFT JOIN RightTable R ON S.ID =R.ID AND S.MID =R.MID WHERE R.TIM >0 AND 
S.MOB IS NOT NULL

第1步:链接所有表

您可以根据需要查找任意数量的表.

$ lookup - 查询中每个表一个

$ unwind - 因为数据是非正规化的,否则包装在数组中

Python代码..

db.LeftTable.aggregate([
                        # connect all tables

                        {"$lookup": {
                          "from": "RightTable",
                          "localField": "ID",
                          "foreignField": "ID",
                          "as": "R"
                        }},
                        {"$unwind": "R"}

                        ])

第2步:定义所有条件

$ project:在此定义所有条件语句,以及您要选择的所有变量.

Python代码..

db.LeftTable.aggregate([
                        # connect all tables

                        {"$lookup": {
                          "from": "RightTable",
                          "localField": "ID",
                          "foreignField": "ID",
                          "as": "R"
                        }},
                        {"$unwind": "R"},

                        # define conditionals + variables

                        {"$project": {
                          "midEq": {"$eq": ["$MID", "$R.MID"]},
                          "ID": 1, "MOB": 1, "MID": 1
                        }}
                        ])

第3步:加入所有条件

$ match - 使用OR或AND等加入所有条件.可以有多个这些条件.

$ project:取消定义所有条件

Python代码..

db.LeftTable.aggregate([
                        # connect all tables

                        {"$lookup": {
                          "from": "RightTable",
                          "localField": "ID",
                          "foreignField": "ID",
                          "as": "R"
                        }},
                        {"$unwind": "$R"},

                        # define conditionals + variables

                        {"$project": {
                          "midEq": {"$eq": ["$MID", "$R.MID"]},
                          "ID": 1, "MOB": 1, "MID": 1
                        }},

                        # join all conditionals

                        {"$match": {
                          "$and": [
                            {"R.TIM": {"$gt": 0}}, 
                            {"MOB": {"$exists": True}},
                            {"midEq": {"$eq": True}}
                        ]}},

                        # undefine conditionals

                        {"$project": {
                          "midEq": 0
                        }}

                        ])

几乎任何表,条件和连接的组合都可以这种方式完成.



8> Snowburnt..:

这取决于你想要做什么.

您目前已将其设置为规范化数据库,这很好,并且您采用的方式也是合适的.

但是,还有其他方法可以做到这一点.

您可以拥有一个帖子集合,其中包含对每个帖子的嵌入评论,并引用您可以迭代查询的用户.您可以将用户的名称与注释一起存储,您可以将它们全部存储在一个文档中.

NoSQL的用途是它设计用于灵活的模式和非常快速的读写.在一个典型的大数据农场中,数据库是最大的瓶颈,你拥有的数据库引擎比应用程序和前端服务器少......它们更昂贵但功能更强大,硬盘空间也相对便宜.规范化来自于试图节省空间的概念,但它带来了使数据库执行复杂连接和验证关系完整性,执行级联操作的成本.如果他们正确地设计数据库,所有这些都会使开发人员感到头疼.

使用NoSQL,如果您认为冗余和存储空间不是问题,因为它们的成本(更新所需的处理器时间和存储额外数据的硬盘驱动器成本),非规范化不是问题(对于嵌入式阵列而言)数十万个项目可能是性能问题,但大多数时候这不是问题).此外,您将为每个数据库集群提供多个应用程序和前端服务器.让他们完成连接的繁重工作,让数据库服务器坚持读写.

TL; DR:你正在做的事情很好,还有其他方法可以做到.查看mongodb文档的数据模型模式以获取一些很好的示例.http://docs.mongodb.org/manual/data-modeling/


"规范化来自于试图节省空间的概念"我对此提出质疑.恕我直言规范化来自避免冗余的概念.假设您存储用户的名称以及博客帖子.如果她结婚怎么办?在非规范化模型中,您将不得不浏览所有帖子并更改名称.在标准化模型中,您通常会更改一条记录.

9> jarry jafery..:

您可以使用3.2版本中提供的查找在Mongo中加入两个集合.在您的情况下,查询将是

db.comments.aggregate({
    $lookup:{
        from:"users",
        localField:"uid",
        foreignField:"uid",
        as:"users_comments"
    }
})

或者您也可以加入用户,然后会有一点变化,如下所示.

db.users.aggregate({
    $lookup:{
        from:"comments",
        localField:"uid",
        foreignField:"uid",
        as:"users_comments"
    }
})

它将与SQL中的左右连接一样工作.



10> Pickels..:

有一个规范,许多驱动程序支持称为DBRef.

DBRef是用于在文档之间创建引用的更正式的规范.DBRefs(通常)包括集合名称和对象ID.如果集合可以从一个文档更改为下一个文档,则大多数开发人员仅使用DBRef.如果您引用的集合始终相同,则上述手动引用更有效.

摘自MongoDB文档:数据模型>数据模型参考> 数据库参考



11> GoutamS..:

$ lookup(汇总)

对同一数据库中的未分片集合执行左外部联接,以从“联接”集合中过滤文档以进行处理。在每个输入文档中,$ lookup阶段都会添加一个新的数组字段,其元素是“ joined”集合中的匹配文档。$ lookup阶段将这些经过重整的文档传递到下一个阶段。$ lookup阶段具有以下语法:

平等比赛

为了在输入文档的字段与“ joined”集合的文档的字段之间执行相等匹配,$ lookup阶段具有以下语法:

{
   $lookup:
     {
       from: ,
       localField: ,
       foreignField: ,
       as: 
     }
}

该操作将对应于以下伪SQL语句:

SELECT *, 
FROM collection
WHERE  IN (SELECT 
                               FROM 
                               WHERE  );

Mongo URL



12> 小智..:

3.2.6之前,Mongodb不像mysql那样支持连接查询.以下解决方案适合您.

 db.getCollection('comments').aggregate([
        {$match : {pid : 444}},
        {$lookup: {from: "users",localField: "uid",foreignField: "uid",as: "userData"}},
   ])

推荐阅读
ar_wen2402851455
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有