sql >> Database >  >> NoSQL >> MongoDB

Koppelen en maken van MongoDB-joins met SQL:deel 3

Meerdere JOINS in een enkele zoekopdracht

Meerdere JOINS worden normaal gesproken geassocieerd met meerdere collecties, maar je moet een basiskennis hebben van hoe de INNER JOIN werkt (zie mijn eerdere berichten over dit onderwerp). Naast onze twee collecties die we eerder hadden; eenheden en studenten, laten we een derde collectie toevoegen en deze als sport bestempelen. Vul de sportcollectie met onderstaande gegevens:

{
    "_id" : 1,"tournamentsPlayed" : 6,
    "gamesParticipated" : [{"hockey" : "midfielder","football" : "stricker","handball" : "goalkeeper"}],
    "sportPlaces" : ["Stafford Bridge","South Africa", "Rio Brazil"]
}
{
    "_id" : 2,"tournamentsPlayed" : 3,
    "gamesParticipated" : [{"hockey" : "goalkeeper","football" : "stricker", "handball" : "midfielder"}],
    "sportPlaces" : ["Ukraine","India", "Argentina"]
}
{
    "_id" : 3,"tournamentsPlayed" : 10,
    "gamesParticipated" : [{"hockey" : "stricker","football" : "goalkeeper","tabletennis" : "doublePlayer"}],
    "sportPlaces" : ["China","Korea","France"]
}

We willen bijvoorbeeld alle gegevens voor een student retourneren met een veldwaarde _id gelijk aan 1. Normaal gesproken zouden we een query schrijven om de veldwaarde _id op te halen uit de verzameling studenten, en vervolgens de geretourneerde waarde gebruiken om te zoeken naar gegevens in de andere twee collecties. Dit zal daarom niet de beste optie zijn, vooral als het om een ​​grote set documenten gaat. Een betere benadering zou zijn om de SQL-functie van het Studio3T-programma te gebruiken. We kunnen onze MongoDB opvragen met het normale SQL-concept en vervolgens proberen de resulterende Mongo-shellcode grof af te stemmen op onze specificatie. Laten we bijvoorbeeld alle gegevens ophalen met _id gelijk aan 1 uit alle verzamelingen:

SELECT  *
  FROM students
    INNER JOIN units
      ON students._id = units._id
    INNER JOIN sports
      ON students._id = sports._id
  WHERE students._id = 1;

Het resulterende document is:

{ 
    "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"}
    }, 
    "sports" : {
        "_id" : NumberInt(1),"tournamentsPlayed" : NumberInt(6), 
        "gamesParticipated" : [{"hockey" : "midfielder", "football" : "striker","handball" : "goalkeeper"}], 
        "sportPlaces" : ["Stafford Bridge","South Africa","Rio Brazil"]
    }
}

Op het tabblad Querycode is de corresponderende MongoDB-code:

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}}, 
        { "$lookup" : {"localField" : "students._id","from" : "sports", "foreignField" : "_id","as" : "sports"}}, 
        { "$unwind" : {"path" : "$sports", "preserveNullAndEmptyArrays" : false}}, 
        { "$match" : {"students._id" : NumberLong(1)}}
    ]
);

Als ik naar het geretourneerde document kijk, ben ik persoonlijk niet zo blij met de gegevensstructuur, vooral met ingesloten documenten. Zoals je kunt zien, zijn er _id-velden geretourneerd en voor de eenheden hoeven we het cijferveld mogelijk niet in de eenheden in te sluiten.

We zouden een eenhedenveld willen hebben met ingesloten eenheden en geen andere velden. Dit leidt ons naar het grove melodiegedeelte. Kopieer, net als in de vorige berichten, de code met behulp van het kopieerpictogram en ga naar het aggregatievenster, plak de inhoud met het plakpictogram.

Allereerst moet de operator $match de eerste fase zijn, dus verplaats deze naar de eerste positie en heb zoiets als dit:

Klik op het tabblad van de eerste fase en wijzig de zoekopdracht in:

{
    "_id" : NumberLong(1)
}

Vervolgens moeten we de query verder aanpassen om veel insluitstadia van onze gegevens te verwijderen. Om dit te doen, voegen we nieuwe velden toe om gegevens vast te leggen voor de velden die we willen elimineren, d.w.z.:

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

Zoals u kunt zien, hebben we bij het fijnafstemmingsproces nieuwe veldeenheden geïntroduceerd die de inhoud van de vorige aggregatiepijplijn zullen overschrijven met cijfers als een ingebed veld. Verder hebben we een _id-veld gemaakt om aan te geven dat de gegevens betrekking hadden op documenten in de collecties met dezelfde waarde. De laatste $project-fase is om het _id-veld in het sportdocument te verwijderen, zodat we de onderstaande gegevens netjes kunnen presenteren.

{  "_id" : NumberInt(1), 
    "students" : {"name" : "James Washington", "age" : 15.0,  "grade" : "A", "score" : 10.5}, 
    "units" : {"Maths" : "A","English" : "A", "Science" : "A","History" : "B"}, 
    "sports" : {
        "tournamentsPlayed" : NumberInt(6), 
        "gamesParticipated" : [{"hockey" : "midfielder","football" : "striker","handball" : "goalkeeper"}],  
        "sportPlaces" : ["Stafford Bridge", "South Africa", "Rio Brazil"]
        }
}

We kunnen ook beperken op welke velden moeten worden geretourneerd vanuit het SQL-oogpunt. We kunnen bijvoorbeeld de naam van de student, de eenheden die deze student doet en het aantal gespeelde toernooien met meerdere JOINS retourneren met de onderstaande code:

SELECT  students.name, units.grades, sports.tournamentsPlayed
  FROM students
    INNER JOIN units
      ON students._id = units._id
    INNER JOIN sports
      ON students._id = sports._id
  WHERE students._id = 1;

Dit geeft ons niet het meest geschikte resultaat. Kopieer het dus zoals gewoonlijk en plak het in het aggregatievenster. We stemmen af ​​met de onderstaande code om het juiste resultaat te krijgen.

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

Dit aggregatieresultaat van het SQL JOIN-concept geeft ons een nette en representatieve gegevensstructuur die hieronder wordt weergegeven.

{ 
    "name" : "James Washington", 
    "grades" : {"Maths" : "A", "English" : "A", "Science" : "A", "History" : "B"}, 
    "tournamentsPlayed" : NumberInt(6)
}

Vrij eenvoudig, toch? De gegevens zijn vrij toonbaar alsof ze in een enkele verzameling als een enkel document zijn opgeslagen.

LINKER BUITENSTE JOIN

De LEFT OUTER JOIN wordt normaal gesproken gebruikt om documenten weer te geven die niet voldoen aan de meest afgebeelde relatie. De resulterende set van een LEFT OUTER-join bevat alle rijen van beide collecties die voldoen aan de WHERE-clausulecriteria, hetzelfde als een INNER JOIN-resultaatset. Bovendien worden alle documenten uit de linkercollectie die geen overeenkomende documenten in de rechtercollectie hebben, ook opgenomen in de resultatenset. De velden die worden geselecteerd in de tabel aan de rechterkant, retourneren NULL-waarden. Documenten in de rechtercollectie die geen overeenkomende criteria hebben uit de linkercollectie, worden echter niet geretourneerd.

Bekijk deze twee collecties eens:

studenten

{"_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" : 4,"name" : "Mary Muthoni","age" : 16.0,"grade" : "A","score" : 11.5}

Eenheden

{"_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"}

In de studentencollectie hebben we geen _id veldwaarde ingesteld op 3, maar in de eenhedencollectie die we hebben. Evenzo is er geen _id-veldwaarde 4 in de verzameling eenheden. Als we de studentencollectie gebruiken als onze linkeroptie in de JOIN-aanpak met de onderstaande vraag:

SELECT *
  FROM students
    LEFT OUTER JOIN units
      ON students._id = units._id

Met deze code krijgen we het volgende resultaat:

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

Het tweede document heeft het veld eenheden niet omdat er geen overeenkomend document in de verzameling eenheden was. Voor deze SQL-query is de corresponderende Mongo-code

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

Natuurlijk hebben we geleerd over fine-tuning, zodat u door kunt gaan en de aggregatiepijplijn kunt herstructureren om het gewenste eindresultaat te krijgen. SQL is een zeer krachtig hulpmiddel voor wat betreft databasebeheer. Het is een breed onderwerp op zich, je kunt ook proberen de IN- en GROUP BY-clausules te gebruiken om de corresponderende code voor MongoDB te krijgen en te zien hoe het werkt.

Conclusie

Het wennen aan een nieuwe (database)technologie naast degene waarmee u gewend bent te werken, kan veel tijd kosten. Relationele databases komen nog steeds vaker voor dan de niet-relationele. Desalniettemin zijn er met de introductie van MongoDB dingen veranderd en mensen willen het graag zo snel mogelijk leren vanwege de bijbehorende krachtige prestaties.

MongoDB helemaal opnieuw leren kan een beetje vervelend zijn, maar we kunnen de kennis van SQL gebruiken om gegevens in MongoDB te manipuleren, de relatieve MongoDB-code te krijgen en deze fijn af te stemmen om de meest geschikte resultaten te krijgen. Een van de tools die beschikbaar is om dit te verbeteren is Studio 3T. Het biedt twee belangrijke functies die de werking van complexe gegevens vergemakkelijken, namelijk:SQL-queryfunctie en de aggregatie-editor. Door zoekopdrachten nauwkeurig af te stemmen, krijgt u niet alleen het beste resultaat, maar verbetert u ook de prestaties op het gebied van tijdbesparing.


  1. Bestand doorgeven aan actieve taak / achtergrondtaak

  2. hoe krijg ik alle sleutels en waarden in redis in javascript?

  3. Hoe de laatste N-records in Mongodb te krijgen?

  4. Hoe zorg je voor een uniek item in een array op basis van specifieke velden - mongoDB?