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.

Advertisements

About Bahrudin Hrnjica

PhD in Mechanical Engineering, Microsoft MVP for .NET. Likes .NET, Math, Mechanical Engineering, Evolutionary Algorithms, Blogging.

Posted on 09/08/2011, in .NET, C# and tagged , , . Bookmark the permalink. 1 Comment.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s