MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Verknüpfen und Erstellen von MongoDB-Joins mit SQL:Teil 2

JOIN ist eines der wichtigsten Unterscheidungsmerkmale zwischen SQL- und NoSQL-Datenbanken. In SQL-Datenbanken können wir einen JOIN zwischen zwei Tabellen innerhalb derselben oder verschiedener Datenbanken durchführen. Dies ist jedoch bei MongoDB nicht der Fall, da es JOIN-Operationen zwischen zwei Sammlungen in derselben Datenbank zulässt.

Die Art und Weise, wie Daten in MongoDB dargestellt werden, macht es fast unmöglich, sie von einer Sammlung zu einer anderen zu beziehen, außer wenn grundlegende Skriptabfragefunktionen verwendet werden. MongoDB denormalisiert Daten entweder durch Speichern verwandter Elemente in einem separaten Dokument oder verknüpft Daten in einem anderen separaten Dokument.

Man könnte diese Daten in Beziehung setzen, indem man manuelle Referenzen verwendet, wie das _id-Feld eines Dokuments, das in einem anderen Dokument als Referenz gespeichert ist. Trotzdem muss man mehrere Abfragen machen, um einige benötigte Daten abzurufen, was den Vorgang etwas mühsam macht.

Wir entscheiden uns daher für das JOIN-Konzept, das die Verknüpfung der Daten erleichtert. Der JOIN-Vorgang in MongoDB wird durch die Verwendung des $lookup-Operators erreicht, der in Version 3.2 eingeführt wurde.

$lookup-Operator

Die Hauptidee hinter dem JOIN-Konzept besteht darin, eine Korrelation zwischen Daten in einer Sammlung zu einer anderen herzustellen. Die grundlegende Syntax des $lookup-Operators lautet:

{
   $lookup:
     {
       from: <collection to join>,
       localField: <field from the input documents>,
       foreignField: <field from the documents of the "from" collection>,
       as: <output array field>
     }
}

In Bezug auf die SQL-Kenntnisse wissen wir immer, dass das Ergebnis einer JOIN-Operation eine separate Zeile ist, die alle Felder aus der lokalen und der fremden Tabelle verknüpft. Bei MongoDB ist dies insofern anders, als die Ergebnisdokumente als Array von lokalen Sammlungsdokumenten hinzugefügt werden. Lassen Sie uns zum Beispiel zwei Sammlungen haben; „Studenten“ und „Einheiten“

Schüler

{"_id" : 1,"name" : "James Washington","age" : 15.0,"grade" : "A","score" : 10.5}
{"_id" : 2,"name" : "Clinton Ariango","age" : 14.0,"grade" : "B","score" : 7.5}
{"_id" : 3,"name" : "Mary Muthoni","age" : 16.0,"grade" : "A","score" : 11.5}

Einheiten

{"_id" : 1,"Maths" : "A","English" : "A","Science" : "A","History" : "B"}
{"_id" : 2,"Maths" : "B","English" : "B","Science" : "A","History" : "B"}
{"_id" : 3,"Maths" : "A","English" : "A","Science" : "A","History" : "A"}

Wir können die Einheiten der Schüler mit den jeweiligen Noten abrufen, indem wir den $lookup-Operator mit dem JOIN-Ansatz .d.h.

verwenden
db.getCollection('students').aggregate([{
$lookup:
    {
        from: "units",
        localField: "_id",
        foreignField : "_id",
        as: "studentUnits"
    }
}])

Was uns die folgenden Ergebnisse liefern wird:

{"_id" : 1,"name" : "James Washington","age" : 15,"grade" : "A","score" : 10.5,
    "studentUnits" : [{"_id" : 1,"Maths" : "A","English" : "A","Science" : "A","History" : "B"}]}
{"_id" : 2,"name" : "Clinton Ariango","age" : 14,"grade" : "B","score" : 7.5,
    "studentUnits" : [{"_id" : 2,"Maths" : "B","English" : "B","Science" : "A","History" : "B"}]}
{"_id" : 3,"name" : "Mary Muthoni","age" : 16,"grade" : "A","score" : 11.5,
    "studentUnits" : [{"_id" : 3,"Maths" : "A","English" : "A","Science" : "A","History" : "A"}]}

Wie bereits erwähnt, werden wir, wenn wir einen JOIN unter Verwendung des SQL-Konzepts durchführen, mit separaten Dokumenten in der Studio3T-Plattform zurückgegeben, d. h.

SELECT *
  FROM students
    INNER JOIN units
      ON students._id = units._id

Ist ein Äquivalent von

db.getCollection("students").aggregate(
    [
        { 
            "$project" : {
                "_id" : NumberInt(0), 
                "students" : "$$ROOT"
            }
        }, 
        { 
            "$lookup" : {
                "localField" : "students._id", 
                "from" : "units", 
                "foreignField" : "_id", 
                "as" : "units"
            }
        }, 
        { 
            "$unwind" : {
                "path" : "$units", 
                "preserveNullAndEmptyArrays" : false
            }
        }
    ]
);

Die obige SQL-Abfrage gibt die folgenden Ergebnisse zurück:

{ "students" : {"_id" : NumberInt(1),"name" : "James Washington","age" : 15.0,"grade" : "A","score" : 10.5}, 
    "units" : {"_id" : NumberInt(1),"Maths" : "A","English" : "A","Science" : "A","History" : "B"}}
{ "students" : {"_id" : NumberInt(2), "name" : "Clinton Ariango","age" : 14.0,"grade" : "B","score" : 7.5 }, 
    "units" : {"_id" : NumberInt(2),"Maths" : "B","English" : "B","Science" : "A","History" : "B"}}
{ "students" : {"_id" : NumberInt(3),"name" : "Mary Muthoni","age" : 16.0,"grade" : "A","score" : 11.5},
"units" : {"_id" : NumberInt(3),"Maths" : "A","English" : "A","Science" : "A","History" : "A"}}

Die Leistungsdauer hängt natürlich von der Struktur Ihrer Abfrage ab. Wenn Sie beispielsweise viele Dokumente in einer Sammlung übereinander haben, sollten Sie die Aggregation aus der Sammlung mit weniger Dokumenten durchführen und dann in der Sammlung mit mehr Dokumenten suchen. Auf diese Weise ist eine Suche nach dem ausgewählten Feld aus der kleineren Dokumentensammlung ziemlich optimal und nimmt weniger Zeit in Anspruch, als mehrere Suchen nach einem ausgewählten Feld in der Sammlung mit mehr Dokumenten durchzuführen. Es ist daher ratsam, die kleinere Sammlung an die erste Stelle zu setzen.

Bei einer relationalen Datenbank spielt die Reihenfolge der Datenbanken keine Rolle, da die meisten SQL-Interpreter über Optimierer verfügen, die Zugriff auf zusätzliche Informationen haben, um zu entscheiden, welche die erste sein soll.

Im Fall von MongoDB müssen wir einen Index verwenden, um die JOIN-Operation zu erleichtern. Wir alle wissen, dass alle MongoDB-Dokumente einen _id-Schlüssel haben, der für ein relationales DBM als Primärschlüssel betrachtet werden kann. Ein Index bietet eine bessere Möglichkeit, die Datenmenge zu reduzieren, auf die zugegriffen werden muss, und unterstützt die Operation, wenn er im $lookup-Fremdschlüssel verwendet wird.

Um einen Index zu verwenden, müssen wir in der Aggregationspipeline sicherstellen, dass $match in der ersten Phase ausgeführt wird, um Dokumente herauszufiltern, die den Kriterien nicht entsprechen. Wenn wir zum Beispiel das Ergebnis für den Schüler mit dem Feldwert _id gleich 1 abrufen möchten:

select * 
from students 
  INNER JOIN units 
    ON students._id = units._id 
      WHERE students._id = 1;

Der entsprechende MongoDB-Code, den Sie in diesem Fall erhalten, lautet:

db.getCollection("students").aggregate(
[{"$project" : { "_id" : NumberInt(0), "students" : "$$ROOT" }}, 
     {  "$lookup" : {"localField" : "students._id", "from" : "units",  "foreignField" : "_id",  "as" : "units"} }, 
     { "$unwind" : { "path" : "$units","preserveNullAndEmptyArrays" : false } }, 
      { "$match" : {"students._id" : NumberLong(1) }}
    ]);

Das zurückgegebene Ergebnis für die obige Abfrage lautet:

{"_id" : 1,"name" : "James Washington","age" : 15,"grade" : "A","score" : 10.5,
    "studentUnits" : [{"_id" : 1,"Maths" : "A","English" : "A","Science" : "A","History" : "B"}]}

Wenn wir die $match-Stufe nicht verwenden oder eher nicht in der ersten Stufe, wenn wir mit der EXPLAIN-Funktion prüfen, erhalten wir die COLLSCAN-Stufe ebenfalls enthalten. Die Durchführung eines COLLSCAN für eine große Anzahl von Dokumenten nimmt im Allgemeinen viel Zeit in Anspruch. Wir beschließen dabei, ein Indexfeld zu verwenden, das in der EXPLAIN-Funktion nur die IXSCAN-Stufe betrifft. Letzteres hat den Vorteil, dass wir einen Index in den Dokumenten prüfen und nicht alle Dokumente durchsuchen; Es wird nicht lange dauern, die Ergebnisse zurückzugeben. Möglicherweise haben Sie eine andere Datenstruktur wie:

{    "_id" : NumberInt(1), 
    "grades" : {"Maths" : "A", "English" : "A",  "Science" : "A", "History" : "B"
    }
}

Möglicherweise möchten wir die Noten als verschiedene Entitäten in einem Array zurückgeben, anstatt als ganzes eingebettetes Notenfeld.

Nachdem wir die obige SQL-Abfrage geschrieben haben, müssen wir den resultierenden MongoDB-Code ändern. Klicken Sie dazu rechts unten auf das Kopiersymbol, um den Aggregationscode zu kopieren:

Gehen Sie als Nächstes zur Registerkarte „Aggregation“ und im angezeigten Bereich befindet sich ein Symbol zum Einfügen. Klicken Sie darauf, um den Code einzufügen.

Klicken Sie auf die Zeile $match und dann auf den grünen Aufwärtspfeil, um die Stufe als erste Stufe nach oben zu verschieben. Sie müssen jedoch zuerst einen Index in Ihrer Sammlung erstellen, wie zum Beispiel:

db.students.createIndex(
   { _id: 1 },
   { name: studentId }
)

Sie erhalten das folgende Codebeispiel:

db.getCollection("students").aggregate(
    [{ "$match" : {"_id" : 1.0}},
  { "$project" : {"_id" : NumberInt(0),"students" : "$$ROOT"}}, 
      { "$lookup" : {"localField" : "students._id","from" : "units","foreignField" : "_id","as" : "units"}}, 
      { "$unwind" : {"path" : "$units", "preserveNullAndEmptyArrays" : false}}
    ]
Multiplenines Become a MongoDB DBA – Bringing MongoDB to ProductionErfahren Sie, was Sie wissen müssen, um MongoDBDownload for Free bereitzustellen, zu überwachen, zu verwalten und zu skalieren

Mit diesem Code erhalten wir das folgende Ergebnis:

{ "students" : {"_id" : NumberInt(1), "name" : "James Washington","age" : 15.0,"grade" : "A", "score" : 10.5}, 
    "units" : {"_id" : NumberInt(1), "grades" : {"Maths" : "A", "English" : "A", "Science" : "A",  "History" : "B"}}}

Aber alles, was wir brauchen, ist, die Noten als separate Dokumententität im zurückgegebenen Dokument zu haben und nicht wie im obigen Beispiel. Wir werden daher die Stufe $addfields hinzufügen, daher den folgenden Code.

db.getCollection("students").aggregate(
    [{ "$match" : {"_id" : 1.0}},
  { "$project" : {"_id" : NumberInt(0),"students" : "$$ROOT"}}, 
      { "$lookup" : {"localField" : "students._id","from" : "units","foreignField" : "_id","as" : "units"}}, 
      { "$unwind" : {"path" : "$units", "preserveNullAndEmptyArrays" : false}},
      { "$addFields" : {"units" : "$units.grades"} }]

Die resultierenden Dokumente sind dann:

{
"students" : {"_id" : NumberInt(1), "name" : "James Washington", "grade" : "A","score" : 10.5}, 
     "units" : {"Maths" : "A", "English" : "A",  "Science" : "A", "History" : "B"}
}

Die zurückgegebenen Daten sind recht ordentlich, da wir eingebettete Dokumente aus der Sammlung der Einheiten als separates Feld eliminiert haben.

In unserem nächsten Tutorial werden wir uns Abfragen mit mehreren Joins ansehen.