sql >> Databáze >  >> NoSQL >> MongoDB

Propojování a vytváření spojení MongoDB pomocí SQL:Část 3

Vícenásobné JOINY v jednom dotazu

Vícenásobné JOINY jsou normálně spojeny s více sbírkami, ale musíte mít základní znalosti o tom, jak INNER JOIN funguje (viz moje předchozí příspěvky na toto téma). Kromě našich dvou sbírek, které jsme měli dříve; jednotek a studentů, přidejme třetí kolekci a označme ji jako sportovní. Naplňte sportovní kolekci níže uvedenými údaji:

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

Chtěli bychom například vrátit všechna data pro studenta s hodnotou pole _id rovnou 1. Normálně bychom napsali dotaz k načtení hodnoty pole _id z kolekce studentů a poté vrácenou hodnotu použili k dotazu na údaje v dalších dvou kolekcích. V důsledku toho to nebude nejlepší volba, zejména pokud se jedná o velký soubor dokumentů. Lepším přístupem by bylo použití funkce SQL programu Studio3T. Můžeme se dotazovat na naši MongoDB s normálním konceptem SQL a pak se pokusit vyladit výsledný kód shellu Mongo tak, aby vyhovoval naší specifikaci. Například načteme všechna data s _id rovným 1 ze všech kolekcí:

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

Výsledný dokument bude:

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

Na kartě Kód dotazu bude odpovídající kód MongoDB:

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)}}
    ]
);

Při pohledu do vráceného dokumentu osobně nejsem příliš spokojen s datovou strukturou, zejména u vložených dokumentů. Jak můžete vidět, jsou vrácena pole _id a pro jednotky nemusíme potřebovat, aby bylo pole hodnocení vloženo do jednotek.

Chtěli bychom mít pole jednotek s vloženými jednotkami a ne žádná jiná pole. To nás vede k části hrubého ladění. Stejně jako v předchozích příspěvcích zkopírujte kód pomocí poskytnuté ikony kopírování a přejděte do podokna agregace, vložte obsah pomocí ikony vložit.

Za prvé, operátor $match by měl být první fází, takže jej přesuňte na první pozici a získejte něco takového:

Klepněte na kartu první fáze a upravte dotaz na:

{
    "_id" : NumberLong(1)
}

Poté musíme dotaz dále upravit, abychom odstranili mnoho fází vkládání našich dat. Za tímto účelem přidáváme nová pole pro zachycení dat pro pole, která chceme odstranit, tj.:

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}}
        ]
);

Jak můžete vidět, v procesu jemného ladění jsme zavedli nové jednotky polí, které přepíší obsah předchozího agregačního potrubí se známkami jako vloženým polem. Dále jsme vytvořili pole _id, které označuje, že data byla ve vztahu k jakýmkoli dokumentům ve sbírkách se stejnou hodnotou. Poslední fází $projektu je odstranění pole _id ve sportovním dokumentu, abychom mohli mít přehledně prezentovaná data, jak je uvedeno níže.

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

Můžeme také omezit, která pole mají být vrácena z hlediska SQL. Například můžeme vrátit jméno studenta, jednotky, které tento student dělá, a počet turnajů odehraných pomocí více JOINS pomocí kódu níže:

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;

To nám nedává nejvhodnější výsledek. Takže jako obvykle jej zkopírujte a vložte do podokna agregace. Pomocí níže uvedeného kódu doladíme, abychom získali odpovídající výsledek.

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"}
        }}
        ]
);

Tento výsledek agregace z konceptu SQL JOIN nám poskytuje úhlednou a prezentovatelnou datovou strukturu uvedenou níže.

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

Docela jednoduché, že? Data jsou docela prezentovatelná, jako by byla uložena v jedné kolekci jako jeden dokument.

LEVÉ VNĚJŠÍ PŘIPOJENÍ

LEVÉ VNĚJŠÍ SPOJENÍ se běžně používá k zobrazení dokumentů, které neodpovídají nejčastěji zobrazovanému vztahu. Výsledná sada spojení LEFT OUTER obsahuje všechny řádky z obou kolekcí, které splňují kritéria klauzule WHERE, stejně jako sada výsledků INNER JOIN. Kromě toho všechny dokumenty z levé kolekce, které nemají odpovídající dokumenty v pravé kolekci, budou také zahrnuty do sady výsledků. Pole vybraná z tabulky na pravé straně vrátí hodnoty NULL. Žádné dokumenty v pravé kolekci, které nemají odpovídající kritéria z levé kolekce, však nebudou vráceny.

Podívejte se na tyto dvě kolekce:

studenti

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

Jednotky

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

V kolekci studentů nemáme hodnotu pole _id nastavenou na 3, ale v kolekci jednotek máme. Podobně neexistuje žádná hodnota pole _id 4 in v kolekci jednotek. Pokud použijeme kolekci studentů jako naši levou možnost v přístupu JOIN s dotazem níže:

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

S tímto kódem získáme následující výsledek:

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

Druhý dokument nemá pole jednotek, protože v kolekci jednotek nebyl žádný odpovídající dokument. Pro tento SQL dotaz bude odpovídající Mongo kód

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}
        }
    ]
);

Samozřejmě jsme se dozvěděli o jemném ladění, takže můžete pokračovat a restrukturalizovat agregační potrubí tak, aby vyhovovalo konečnému výsledku, který byste chtěli. SQL je velmi mocný nástroj pro správu databází. Je to samo o sobě široké téma, můžete také zkusit použít klauzule IN a GROUP BY, abyste získali odpovídající kód pro MongoDB a viděli, jak to funguje.

Závěr

Zvyknutí si na novou (databázovou) technologii navíc k té, se kterou jste zvyklí pracovat, může zabrat spoustu času. Relační databáze jsou stále běžnější než ty nerelační. Se zavedením MongoDB se však věci změnily a lidé by se jej rádi naučili co nejrychleji, protože s ním souvisí vysoký výkon.

Naučit se MongoDB od nuly může být trochu zdlouhavé, ale můžeme využít znalosti SQL k manipulaci s daty v MongoDB, získat relativní kód MongoDB a doladit jej, abychom získali co nejvhodnější výsledky. Jedním z nástrojů, který je k dispozici pro zlepšení tohoto stavu, je Studio 3T. Nabízí dvě důležité funkce, které usnadňují práci se složitými daty, to jest:funkce dotazů SQL a editor agregace. Dotazy jemného ladění vám nejen zajistí nejlepší výsledek, ale také zlepší výkon z hlediska úspory času.


  1. Proč má Redis funkce Pub/Sub?

  2. RuntimeWarning:Spouštíte worker s oprávněními superuživatele:toto se absolutně nedoporučuje

  3. Redis - Jak souvisí klíč HASH a SET a ZSET na uložení CrudRepository?

  4. Najděte dokumenty s polem, které neobsahuje konkrétní hodnotu