LEFT OUTER JOIN in LINQ

后端 未结 22 2516
臣服心动
臣服心动 2020-11-21 04:49

How to perform left outer join in C# LINQ to objects without using join-on-equals-into clauses? Is there any way to do that with where clause? Corr

相关标签:
22条回答
  • 2020-11-21 05:25

    If you need to join and filter on something, that can be done outside of the join. Filter can be done after creating the collection.

    In this case if I do this in the join condition I reduce the rows that are returned.

    Ternary condition is used (= n == null ? "__" : n.MonDayNote,)

    • If the object is null (so no match), then return what is after the ?. __, in this case.

    • Else, return what is after the :, n.MonDayNote.

    Thanks to the other contributors that is where I started with my own issue.


            var schedLocations = (from f in db.RAMS_REVENUE_LOCATIONS
                  join n in db.RAMS_LOCATION_PLANNED_MANNING on f.revenueCenterID equals
    
                      n.revenueCenterID into lm
    
                  from n in lm.DefaultIfEmpty()
    
                  join r in db.RAMS_LOCATION_SCHED_NOTE on f.revenueCenterID equals r.revenueCenterID
                  into locnotes
    
                  from r in locnotes.DefaultIfEmpty()
                  where f.LocID == nLocID && f.In_Use == true && f.revenueCenterID > 1000
    
                  orderby f.Areano ascending, f.Locname ascending
                  select new
                  {
                      Facname = f.Locname,
                      f.Areano,
                      f.revenueCenterID,
                      f.Locabbrev,
    
                      //  MonNote = n == null ? "__" : n.MonDayNote,
                      MonNote = n == null ? "__" : n.MonDayNote,
                      TueNote = n == null ? "__" : n.TueDayNote,
                      WedNote = n == null ? "__" : n.WedDayNote,
                      ThuNote = n == null ? "__" : n.ThuDayNote,
    
                      FriNote = n == null ? "__" : n.FriDayNote,
                      SatNote = n == null ? "__" : n.SatDayNote,
                      SunNote = n == null ? "__" : n.SunDayNote,
                      MonEmpNbr = n == null ? 0 : n.MonEmpNbr,
                      TueEmpNbr = n == null ? 0 : n.TueEmpNbr,
                      WedEmpNbr = n == null ? 0 : n.WedEmpNbr,
                      ThuEmpNbr = n == null ? 0 : n.ThuEmpNbr,
                      FriEmpNbr = n == null ? 0 : n.FriEmpNbr,
                      SatEmpNbr = n == null ? 0 : n.SatEmpNbr,
                      SunEmpNbr = n == null ? 0 : n.SunEmpNbr,
                      SchedMondayDate = n == null ? dMon : n.MondaySchedDate,
                      LocNotes = r == null ? "Notes: N/A" : r.LocationNote
    
                  }).ToList();
                    Func<int, string> LambdaManning = (x) => { return x == 0 ? "" : "Manning:" + x.ToString(); };
            DataTable dt_ScheduleMaster = PsuedoSchedule.Tables["ScheduleMasterWithNotes"];
            var schedLocations2 = schedLocations.Where(x => x.SchedMondayDate == dMon);
    
    0 讨论(0)
  • 2020-11-21 05:28

    If a database driven LINQ provider is used, a significantly more readable left outer join can be written as such:

    from maintable in Repo.T_Whatever 
    from xxx in Repo.T_ANY_TABLE.Where(join condition).DefaultIfEmpty()
    

    If you omit the DefaultIfEmpty() you will have an inner join.

    Take the accepted answer:

      from c in categories
        join p in products on c equals p.Category into ps
        from p in ps.DefaultIfEmpty()
    

    This syntax is very confusing, and it's not clear how it works when you want to left join MULTIPLE tables.

    Note
    It should be noted that from alias in Repo.whatever.Where(condition).DefaultIfEmpty() is the same as an outer-apply/left-join-lateral, which any (decent) database-optimizer is perfectly capable of translating into a left join, as long as you don't introduce per-row-values (aka an actual outer apply). Don't do this in Linq-2-Objects (because there's no DB-optimizer when you use Linq-to-Objects).

    Detailed Example

    var query2 = (
        from users in Repo.T_User
        from mappings in Repo.T_User_Group
             .Where(mapping => mapping.USRGRP_USR == users.USR_ID)
             .DefaultIfEmpty() // <== makes join left join
        from groups in Repo.T_Group
             .Where(gruppe => gruppe.GRP_ID == mappings.USRGRP_GRP)
             .DefaultIfEmpty() // <== makes join left join
    
        // where users.USR_Name.Contains(keyword)
        // || mappings.USRGRP_USR.Equals(666)  
        // || mappings.USRGRP_USR == 666 
        // || groups.Name.Contains(keyword)
    
        select new
        {
             UserId = users.USR_ID
            ,UserName = users.USR_User
            ,UserGroupId = groups.ID
            ,GroupName = groups.Name
        }
    
    );
    
    
    var xy = (query2).ToList();
    

    When used with LINQ 2 SQL it will translate nicely to the following very legible SQL query:

    SELECT 
         users.USR_ID AS UserId 
        ,users.USR_User AS UserName 
        ,groups.ID AS UserGroupId 
        ,groups.Name AS GroupName 
    FROM T_User AS users
    
    LEFT JOIN T_User_Group AS mappings
       ON mappings.USRGRP_USR = users.USR_ID
    
    LEFT JOIN T_Group AS groups
        ON groups.GRP_ID == mappings.USRGRP_GRP
    

    Edit:

    See also " Convert SQL Server query to Linq query " for a more complex example.

    Also, If you're doing it in Linq-2-Objects (instead of Linq-2-SQL), you should do it the old-fashioned way (because LINQ to SQL translates this correctly to join operations, but over objects this method forces a full scan, and doesn't take advantage of index searches, whyever...):

        var query2 = (
        from users in Repo.T_Benutzer
        join mappings in Repo.T_Benutzer_Benutzergruppen on mappings.BEBG_BE equals users.BE_ID into tmpMapp
        join groups in Repo.T_Benutzergruppen on groups.ID equals mappings.BEBG_BG into tmpGroups
        from mappings in tmpMapp.DefaultIfEmpty()
        from groups in tmpGroups.DefaultIfEmpty()
        select new
        {
             UserId = users.BE_ID
            ,UserName = users.BE_User
            ,UserGroupId = mappings.BEBG_BG
            ,GroupName = groups.Name
        }
    
    );
    
    0 讨论(0)
  • 2020-11-21 05:30

    Perform left outer joins in linq C# // Perform left outer joins

    class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
    
    class Child
    {
        public string Name { get; set; }
        public Person Owner { get; set; }
    }
    public class JoinTest
    {
        public static void LeftOuterJoinExample()
        {
            Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
            Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
            Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
            Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };
    
            Child barley = new Child { Name = "Barley", Owner = terry };
            Child boots = new Child { Name = "Boots", Owner = terry };
            Child whiskers = new Child { Name = "Whiskers", Owner = charlotte };
            Child bluemoon = new Child { Name = "Blue Moon", Owner = terry };
            Child daisy = new Child { Name = "Daisy", Owner = magnus };
    
            // Create two lists.
            List<Person> people = new List<Person> { magnus, terry, charlotte, arlene };
            List<Child> childs = new List<Child> { barley, boots, whiskers, bluemoon, daisy };
    
            var query = from person in people
                        join child in childs
                        on person equals child.Owner into gj
                        from subpet in gj.DefaultIfEmpty()
                        select new
                        {
                            person.FirstName,
                            ChildName = subpet!=null? subpet.Name:"No Child"
                        };
                           // PetName = subpet?.Name ?? String.Empty };
    
            foreach (var v in query)
            {
                Console.WriteLine($"{v.FirstName + ":",-25}{v.ChildName}");
            }
        }
    
        // This code produces the following output:
        //
        // Magnus:        Daisy
        // Terry:         Barley
        // Terry:         Boots
        // Terry:         Blue Moon
        // Charlotte:     Whiskers
        // Arlene:        No Child
    

    https://dotnetwithhamid.blogspot.in/

    0 讨论(0)
  • 2020-11-21 05:32

    take look at this example

    class Person
    {
        public int ID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Phone { get; set; }
    }
    
    class Pet
    {
        public string Name { get; set; }
        public Person Owner { get; set; }
    }
    
    public static void LeftOuterJoinExample()
    {
        Person magnus = new Person {ID = 1, FirstName = "Magnus", LastName = "Hedlund"};
        Person terry = new Person {ID = 2, FirstName = "Terry", LastName = "Adams"};
        Person charlotte = new Person {ID = 3, FirstName = "Charlotte", LastName = "Weiss"};
        Person arlene = new Person {ID = 4, FirstName = "Arlene", LastName = "Huff"};
    
        Pet barley = new Pet {Name = "Barley", Owner = terry};
        Pet boots = new Pet {Name = "Boots", Owner = terry};
        Pet whiskers = new Pet {Name = "Whiskers", Owner = charlotte};
        Pet bluemoon = new Pet {Name = "Blue Moon", Owner = terry};
        Pet daisy = new Pet {Name = "Daisy", Owner = magnus};
    
        // Create two lists.
        List<Person> people = new List<Person> {magnus, terry, charlotte, arlene};
        List<Pet> pets = new List<Pet> {barley, boots, whiskers, bluemoon, daisy};
    
        var query = from person in people
            where person.ID == 4
            join pet in pets on person equals pet.Owner  into personpets
            from petOrNull in personpets.DefaultIfEmpty()
            select new { Person=person, Pet = petOrNull}; 
    
    
    
        foreach (var v in query )
        {
            Console.WriteLine("{0,-15}{1}", v.Person.FirstName + ":", (v.Pet == null ? "Does not Exist" : v.Pet.Name));
        }
    }
    
    // This code produces the following output:
    //
    // Magnus:        Daisy
    // Terry:         Barley
    // Terry:         Boots
    // Terry:         Blue Moon
    // Charlotte:     Whiskers
    // Arlene:
    

    now you are able to include elements from the left even if that element has no matches in the right, in our case we retrived Arlene even he has no matching in the right

    here is the reference

    How to: Perform Left Outer Joins (C# Programming Guide)

    0 讨论(0)
  • 2020-11-21 05:33

    There are three tables: persons, schools and persons_schools, which connects persons to the schools they study in. A reference to the person with id=6 is absent in the table persons_schools. However the person with id=6 is presented in the result lef-joined grid.

    List<Person> persons = new List<Person>
    {
        new Person { id = 1, name = "Alex", phone = "4235234" },
        new Person { id = 2, name = "Bob", phone = "0014352" },
        new Person { id = 3, name = "Sam", phone = "1345" },
        new Person { id = 4, name = "Den", phone = "3453452" },
        new Person { id = 5, name = "Alen", phone = "0353012" },
        new Person { id = 6, name = "Simon", phone = "0353012" }
    };
    
    List<School> schools = new List<School>
    {
        new School { id = 1, name = "Saint. John's school"},
        new School { id = 2, name = "Public School 200"},
        new School { id = 3, name = "Public School 203"}
    };
    
    List<PersonSchool> persons_schools = new List<PersonSchool>
    {
        new PersonSchool{id_person = 1, id_school = 1},
        new PersonSchool{id_person = 2, id_school = 2},
        new PersonSchool{id_person = 3, id_school = 3},
        new PersonSchool{id_person = 4, id_school = 1},
        new PersonSchool{id_person = 5, id_school = 2}
        //a relation to the person with id=6 is absent
    };
    
    var query = from person in persons
                join person_school in persons_schools on person.id equals person_school.id_person
                into persons_schools_joined
                from person_school_joined in persons_schools_joined.DefaultIfEmpty()
                from school in schools.Where(var_school => person_school_joined == null ? false : var_school.id == person_school_joined.id_school).DefaultIfEmpty()
                select new { Person = person.name, School = school == null ? String.Empty : school.name };
    
    foreach (var elem in query)
    {
        System.Console.WriteLine("{0},{1}", elem.Person, elem.School);
    }
    
    0 讨论(0)
  • 2020-11-21 05:34

    Now as an extension method:

    public static class LinqExt
    {
        public static IEnumerable<TResult> LeftOuterJoin<TLeft, TRight, TKey, TResult>(this IEnumerable<TLeft> left, IEnumerable<TRight> right, Func<TLeft, TKey> leftKey, Func<TRight, TKey> rightKey,
            Func<TLeft, TRight, TResult> result)
        {
            return left.GroupJoin(right, leftKey, rightKey, (l, r) => new { l, r })
                 .SelectMany(
                     o => o.r.DefaultIfEmpty(),
                     (l, r) => new { lft= l.l, rght = r })
                 .Select(o => result.Invoke(o.lft, o.rght));
        }
    }
    

    Use like you would normally use join:

    var contents = list.LeftOuterJoin(list2, 
                 l => l.country, 
                 r => r.name,
                (l, r) => new { count = l.Count(), l.country, l.reason, r.people })
    

    Hope this saves you some time.

    0 讨论(0)
提交回复
热议问题