Korištenje LEFT i RIGHT JOIN pomoću LINQ


LINQ je, slobodno možemo reći, uveliko promijenio način programiranja u .NET programskim jezicima na način da je nešto što je prije LINQ-a izgledalo jako komplikovano i vrlo zahtjevno danas sa LINQom jednostavno, lagano i „vrlo prosto“.
O LINQ-u sam najviše napisao blog postova. Skoro da nema posta ovdje koji se nekim dijelom ne dotiče LINQa. Ali, s LINQom uvijek se nešto otkrije i pokuša primjeniti u stvarnom primjeru, a inspiraciju nađemo ili u standradnom SQL jeziku ili je inspiracija pronađena u nekom od postojećih algoritama.
Danas ćemo vidjeti kako se preko LINQa implementira LEFT i RIGHT JOIN, koji predstavljaju osnovne operatore SQL jezika. Šta je JOIN odnosno LEFT i RIGHT JOIN možete pogledati na ovom linku , a jednostavno možemo reći da su to osnovni operatoru spajanja dva ili više skupova podataka.
Obični JOIN vrlo lako možemo implementirati preko LINQ. Npr. pretpostavimo da imamo dva tipa podataka: Vehicle tip i VehicleType tip podataka. Pretpostavimo i to da svaki Vehicle tip sadrži osobinu koja nam govori o kakvom VehicleType tipu je riječ. Prevedeno na domaći jezik možemo kazati da svako Vozilo (eng. Vehicle) pripada nekom tipu vozila (eng. VehicleType). Neka imamo sljedeću deklaraciju tipova podataka:

class VehicleGroup
{
    public int? ID;
    public string Name;

}
class VehicleType
{
    public int? ID;
    public string Name;
    public  int? vehiclegroupID;
}
class Vehicle
{
    public int? ID;
    public string  Name;
    public int? vehicletypeID;
}

Svako vozilo (Vehicle) pripada tipu vozila, a tipovi vozila pripadaju određenoj grupi vozila (VehicleGroup). Kada bi željeli da izlistamo sva vozila sa pripadajućem tipom i grupom, LINQ izraz bi izgedao kao na sljedeći način:

//List all vehicles with coresponded types and groups without LEFT JOIN
var q = from v in vehicles
        from t in types
        where v.vehicletypeID == t.ID
        from g in groups
        where t.vehiclegroupID == g.ID
        select new
        {
            VehicleID = v.ID,
            VehicleName = v.Name,
            VehicleTypeName = t.Name,
            VehicleGroupName = g.Name
        };

Ovdje se može primjetiti da je operator JOIN u LINQ implementiran preko WHERE operatora. Mana ovog izraza jesta ta da u koliko ne postoji Vehicle objekat nekog specifičnog VehicleType tipa odnosno grupe, dotični tip i i grupa se neće pojaviti u rezultatu.
U koliko želimo da izlistamo sve grupe i tipove bez obzira da li imamo Vehicle tip definisan, to nećemo moći gornjom implementacijom. Ista situacija se dešava i u relacijskim bazama podataka pa je nužno koristiti LEFT ili RIGHT JOIN.
Da bi prikazali sve grupe i tipove bez obzira da li za dotične postoji Vehicle tip implementacija LINQa data je na sljedećem listingu:

//List all vehicles with coresponded types and groups with RIGHT JOIN,
//If we want LEFT JOIN from below query order of the collections will be as in the previous query
var q = from grp in groups

        from typ in types
                .Where(t=>t.vehiclegroupID==grp.ID)
                .DefaultIfEmpty()//this is important method for RIGHT JOIN

        from veh in vehicles
                .Where(v=>v.vehicletypeID == typ.ID)
                .DefaultIfEmpty()//this is important method for RIGHT JOIN
        select new
        {
            VehicleID = veh==null?0: veh.ID,
            VehicleName = veh == null ? "unknown" : veh.Name,
            VehicleTypeName = typ==null? "unknown" : typ.Name,
            VehicleGroupName = grp==null? "unknown":grp.Name
        };

Vidimo da je implementacija RIGHT JOIN slična prethodnoj implementaciji. Razlika se sastoji u poretku kolekcija, gdje ovdje idemo od najvišeg tipa po hijearhiji do najnižeg. Druga stvar koju smo ovdje koristili jeste metoda DefaultIfEmpty() koja nam sprečava da LINQ upit “pukne” kada se u procesuiranju dođe do null vrijednosti. Na kraju pravimo novi tip u koji unosimo vrijednosti iz svih kolekcija. Međutim pošto se ovdje može desiti da Vehicle ne postoji za određeni tip ili da tip i vehicle ne postoji za određenu grupu, prilikom inicijalizacije kolekcije sa rezultatom provjeravamo da li je dotični property null. Na ovaj način se također sprečavamo da nam LINQ izraz ne “pukne” u toku procesuiranja. LEFT JOIN se jednostavno implementira na način da ovom upitu zamjenimo poredak kolekcija, odnosno da poredak bude isti kao u prvom upitu,  a ostalo ostaje isto.

Na kraju napravimo jednostavni demo koji će nam demonstrirati prethodno rečeno. Implementirajno prezentirane kolekcije vehicle, types i group sa podacima, ali na taj način da imamo jedan VehicleType koji neće sadržavati ni jedan Vehicle.

private static void InitData(out List groups,out List types,out List vehicles)
        {
            groups = new List()
            {
                new VehicleGroup()
                {
                    ID=0,
                    Name="Unknown"
                },
                new VehicleGroup()
                {
                    ID=1,
                    Name= "Group1"
                },
                new VehicleGroup()
                {
                    ID=2,
                    Name= "Group2"
                }
            };

            types = new List()
            {
                new VehicleType()
                {
                    ID=0,
                    Name="Unknown",
                    vehiclegroupID=0
                },
                new VehicleType()
                {
                    ID=1,
                    Name= "Type1",
                    vehiclegroupID=1

                },
                new VehicleType()
                {
                    ID=2,
                    Name= "Type2",
                    vehiclegroupID=1
                },
                new VehicleType()
                {
                    ID=3,
                    Name= "Type3",
                    vehiclegroupID=2
                },
                new VehicleType()
                {
                    ID=4,
                    Name= "Type4",
                    vehiclegroupID=1
                }
            };
            vehicles = new List()
            {
                new Vehicle()
                {
                    ID=0,
                    Name="Unknown",
                    vehicletypeID=0
                },
                new Vehicle()
                {
                    ID=1,
                    Name= "Vehicle1",
                    vehicletypeID=1

                },
                new Vehicle()
                {
                    ID=2,
                    Name= "Vehicle2",
                    vehicletypeID=2
                },
                new Vehicle()
                {
                    ID=3,
                    Name= "Vehicle3",
                    vehicletypeID=3
                },
                new Vehicle()
                {
                    ID=4,
                    Name= "Vehicle4",
                    vehicletypeID=1
                },
                new Vehicle()
                {
                    ID=5,
                    Name= "Vehicle5",
                    vehicletypeID=2
                },
                new Vehicle()
                {
                    ID=6,
                    Name= "Vehicle6",
                    vehicletypeID=2
                },
                new Vehicle()
                {
                    ID=7,
                    Name= "Vehicle7",
                    vehicletypeID=2
                },
                new Vehicle()
                {
                    ID=8,
                    Name= "Vehicle8",
                    vehicletypeID=2
                },
                new Vehicle()
                {
                    ID=9,
                    Name= "Vehicle9",
                    vehicletypeID=3
                },
                new Vehicle()
                {
                    ID=10,
                    Name= "Vehicle10",
                    vehicletypeID=3
                }
            };

        }

Sada implementirajmo obični i RIGHT JOIN a rezultat pokretanja demo aplikacije vidimo na sljedećoj slici:

Sa slike se može vidjeti da u prvom primjeru imamo 11 stavki, a u drugoj 12. 12-ta stavka je upravo ona stavka koja nije prikazana u prvom slučaju, odnosno nije prikazana stavka sa VehicleType.Name=”Type4″.
Cjelokupan demo može se preuzeti sa ovog linka.

Advertisement