ซ้ายเข้าร่วมใน LINQ


539

วิธีดำเนินการรวมด้านนอกด้านซ้ายใน C # LINQ กับวัตถุโดยไม่ต้องใช้join-on-equals-intoคำสั่ง มีวิธีใดที่จะทำเช่นนั้นด้วยwhereประโยค? แก้ไขปัญหา: สำหรับการเข้าร่วมภายในเป็นเรื่องง่ายและฉันมีวิธีแก้ปัญหาเช่นนี้

List<JoinPair> innerFinal = (from l in lefts from r in rights where l.Key == r.Key
                             select new JoinPair { LeftId = l.Id, RightId = r.Id})

แต่สำหรับด้านนอกเข้าร่วมฉันต้องการวิธีแก้ปัญหา ฉันเป็นแบบนี้ แต่มันไม่ทำงาน

List< JoinPair> leftFinal = (from l in lefts from r in rights
                             select new JoinPair { 
                                            LeftId = l.Id, 
                                            RightId = ((l.Key==r.Key) ? r.Id : 0
                                        })

โดยที่ JoinPair เป็นคลาส:

public class JoinPair { long leftId; long rightId; }

2
คุณสามารถยกตัวอย่างสิ่งที่คุณพยายามทำ
jeroenh

การเข้าร่วมด้านนอกปกติด้านซ้ายเป็นดังนี้: var a = จาก b ใน bb เข้าร่วม c ใน cc บน b.bbbbb เท่ากับ c.ccccc เป็น dd จาก d ใน ddDefaultIfEmpty () select b.sss; คำถามของฉันคือมีวิธีใดที่จะทำเช่นนั้นได้โดยใช้คำสั่ง join-on-equals เป็นบางอย่างเช่น var a = จาก b ใน bb จาก c ใน cc ที่ b.bbb == c.cccc ... และอื่น ๆ .. .
ของเล่น

1
แน่ใจว่ามี แต่คุณควรโพสต์ตัวอย่างของรหัสของคุณที่คุณมีอยู่แล้วเพื่อให้ประชาชนสามารถให้คำตอบที่ดีกว่า
เฉื่อยชา

ฉันกำลังมองหา"ซ้ายยกเว้น " เข้าร่วม (และฉันสับสนกับแนวคิดของ "ออก") คำตอบนี้ใกล้เคียงกับสิ่งที่ฉันต้องการ
ถั่วแดง

โพสต์ที่เกี่ยวข้อง - Linq เข้าร่วม iquery, วิธีการใช้ค่า
RBT

คำตอบ:


598

ตามที่ระบุไว้ใน:

ตัวอย่าง 101 LINQ - การเข้าร่วมด้านนอกด้านซ้าย

var q =
    from c in categories
    join p in products on c.Category equals p.Category into ps
    from p in ps.DefaultIfEmpty()
    select new { Category = c, ProductName = p == null ? "(No products)" : p.ProductName };

7
ฉันกำลังลองสิ่งเดียวกัน แต่ได้รับข้อผิดพลาดในตัวดำเนินการเข้าร่วมซึ่งระบุว่า "ประเภทของหนึ่งในนิพจน์ในส่วนคำสั่งการรวมนั้นไม่ถูกต้อง"
Badhon Jain

3
@jain หากประเภทของคุณแตกต่างกันการเข้าร่วมจะไม่ทำงาน ดังนั้นคีย์ของคุณอาจเป็นประเภทข้อมูลที่แตกต่างกัน มีปุ่มทั้งสองปุ่มที่เป็นตัวอย่างหรือไม่?
Yooakim

2
วิธีแก้ปัญหา Jain คืออะไร? ฉันกำลังเผชิญกับข้อผิดพลาดเดียวกันและประเภทเดียวกันในกรณีของฉันเช่นกัน
Sandeep

1
@Sandeep ตรวจสอบคีย์ของคุณที่คุณเข้าร่วม สมมติว่าเป็นสตริงประเภทและ int แล้วเพียงแค่แปลงสตริงคีย์เป็น int
Ankit


546

หากใช้ตัวให้บริการ LINQ ที่ใช้ฐานข้อมูลจะสามารถเขียนการรวมภายนอกด้านซ้ายที่อ่านได้อย่างมีนัยสำคัญมากขึ้นเช่น:

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

หากคุณเว้นไว้DefaultIfEmpty()คุณจะเข้าร่วมวงใน

รับคำตอบที่ยอมรับ:

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

ไวยากรณ์นี้มีความสับสนมากและยังไม่ชัดเจนว่าจะทำงานอย่างไรเมื่อคุณต้องการออกจากการเข้าร่วมตาราง MULTIPLE

หมายเหตุ
ควรสังเกตว่าfrom alias in Repo.whatever.Where(condition).DefaultIfEmpty()เหมือนกับด้านนอก - สมัคร / ซ้าย - เข้าร่วม - ด้านข้างซึ่งใด ๆ (เหมาะสม) - เครื่องมือเพิ่มประสิทธิภาพฐานข้อมูลที่สมบูรณ์แบบสามารถแปลเป็นการเข้าร่วมด้านซ้ายตราบใดที่คุณไม่แนะนำต่อแถว - ค่า (หรือที่เรียกว่าการใช้ภายนอกจริง) อย่าทำสิ่งนี้ใน Linq-2-Objects (เพราะไม่มี DB-optimizer เมื่อคุณใช้ Linq-to-Objects)

ตัวอย่างรายละเอียด

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();

เมื่อใช้กับ LINQ 2 SQL มันจะแปลข้อความค้นหา SQL ที่อ่านง่ายมากไปได้ดังต่อไปนี้:

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

แก้ไข:

ดูเพิ่มเติม " แปลงแบบสอบถาม SQL Server เป็นแบบสอบถาม Linq " สำหรับตัวอย่างที่ซับซ้อนมากขึ้น

นอกจากนี้หากคุณทำใน Linq-2-Objects (แทน Linq-2-SQL) คุณควรทำแบบเก่า (เพราะ LINQ เป็น SQL แปลสิ่งนี้อย่างถูกต้องเพื่อเข้าร่วมการดำเนินการ แต่เหนือวัตถุวิธีนี้ บังคับให้สแกนแบบเต็มและไม่ใช้ประโยชน์จากการค้นหาดัชนีไม่ว่าจะด้วยเหตุผลใดก็ตาม ... ):

    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
    }

);

21
คำตอบนี้มีประโยชน์จริง ๆ ขอขอบคุณที่เสนอไวยากรณ์ที่เข้าใจได้จริง
Chris Marisic

3
WTB a คำสั่ง LINQ ที่เข้ากันได้กับ NHibernate ... :)
mxmissile

30
LINQ เป็น SQL แปลสิ่งนี้อย่างถูกต้องเพื่อเข้าร่วมการดำเนินการ เหนือวัตถุอย่างไรก็ตามวิธีนี้บังคับให้ทำการสแกนแบบสมบูรณ์นี่คือเหตุผลที่เอกสารประกอบอย่างเป็นทางการนำเสนอโซลูชั่นการเข้าร่วมกลุ่มที่สามารถใช้ประโยชน์จากการแฮชเพื่อค้นหาดัชนี
Tamir Daniely

3
ฉันคิดว่าไวยากรณ์ที่ชัดเจนjoinนั้นอ่านได้ง่ายกว่าและชัดเจนกว่ามากwhereตามมาด้วยDefaultIfEmpty
FindOut_Quran

1
@ user3441905: ตราบใดที่คุณแค่ต้องเข้าร่วมโต๊ะ a กับโต๊ะ b นี่อาจจะเป็น แต่ทันทีที่คุณมีมากกว่านั้นมันจะไม่เป็นเช่นนั้น แต่ถึงแม้จะมีแค่ 2 โต๊ะฉันก็คิดว่ามันเป็นคำพูดมากเกินไป ความคิดเห็นยอดนิยมดูเหมือนจะขัดกับคุณเช่นกันคำตอบนี้เริ่มต้นด้วย 0 เมื่อคำตอบยอดนิยมมีผู้โหวตมากกว่า 90 คนแล้ว
Stefan Steiger

132

การใช้แลมบ์ดาแสดงออก

db.Categories    
  .GroupJoin(db.Products,
      Category => Category.CategoryId,
      Product => Product.CategoryId,
      (x, y) => new { Category = x, Products = y })
  .SelectMany(
      xy => xy.Products.DefaultIfEmpty(),
      (x, y) => new { Category = x.Category, Product = y })
  .Select(s => new
  {
      CategoryName = s.Category.Name,     
      ProductName = s.Product.Name   
  });

8
ทั้งเข้าร่วมและ GroupJoin ไม่สนับสนุนการเข้าร่วมซ้าย เคล็ดลับในการใช้ GroupJoin คือคุณสามารถมีกลุ่มว่างเปล่าแล้วแปลกลุ่มว่างเปล่าเหล่านั้นเป็นค่าว่าง DefaultIfEmpty ก็ทำเช่นนั้นความหมายEnumerable.Empty<Product>.DefaultIfEmpty()จะคืนค่า IEnumerable ที่มีค่าเดียวdefault(Product)คือ
Tamir Daniely

61
ทั้งหมดนี้เพื่อดำเนินการเข้าร่วมซ้าย ??
FindOut_Quran

7
ขอบคุณสำหรับสิ่งนี้! แลมบ์ดามีตัวอย่างการแสดงออกไม่มากนักสิ่งนี้ใช้ได้สำหรับฉัน
Johan Henkens

1
ขอบคุณสำหรับคำตอบ. มันให้สิ่งที่ใกล้เคียงกับ SQL LEFT OUTER LEIN OUTER ที่ฉันเขียนในช่วงหลายปีที่ผ่านมา
John Gathogo

1
ไม่จำเป็นต้องใช้ Select () สุดท้ายอันสุดท้ายอันอนันต์ obj ใน SelectMany () สามารถถูก refactored สำหรับเอาต์พุตเดียวกัน อีกความคิดหนึ่งคือการทดสอบ y เพื่อให้โมฆะเพื่อจำลองการเข้าใกล้ความสมดุล LEFT JOIN ที่ใกล้เคียงยิ่งขึ้น
Denny Jacob

46

ตอนนี้เป็นวิธีการขยาย:

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

ใช้เหมือนปกติคุณจะใช้เข้าร่วม:

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

หวังว่านี่จะช่วยคุณประหยัดเวลา


44

ลองดูตัวอย่างนี้ แบบสอบถามนี้ควรใช้งานได้:

var leftFinal = from left in lefts
                join right in rights on left equals right.Left into leftRights
                from leftRight in leftRights.DefaultIfEmpty()
                select new { LeftId = left.Id, RightId = left.Key==leftRight.Key ? leftRight.Id : 0 };

3
สามารถrเข้าถึงได้ในส่วนคำสั่งที่เลือกหลังจากใช้การเข้าร่วมหรือไม่
Farhad Alizadeh Noori

@FarhadAlizadehNoori ใช่มันสามารถ
Po-ta-toe

ผู้เขียนอาจหมายถึงการใช้ซ้ำrในfromข้อที่สอง ie from r in lrs.DefaultIfEmpty()มิฉะนั้นแบบสอบถามนี้ไม่สมเหตุสมผลและอาจไม่ได้รวบรวมเนื่องจากrอยู่นอกบริบทสำหรับการเลือก
Saeb Amini

@Dart เมื่อฉันอ่านข้อความค้นหาของคุณมันทำให้ฉันนึกถึงภาพยนตร์Clockwiseกับ John Cleese, lol
Matas Vaitkevicius

1
จากซ้ายไปขวาเข้า leftrights ในสิทธิทางด้านซ้ายใน leftRights ... โอ้ ... jeez ไวยากรณ์ของการใช้ภายนอกด้านซ้ายใน LINQ จริงๆไม่ชัดเจน แต่ชื่อเหล่านี้จริงๆทำให้มันยิ่งชัดเจนมากขึ้น
Mike Gledhill

19

การใช้งานการรวมภายนอกด้านนอกโดยวิธีการขยายอาจดูเหมือน

public static IEnumerable<Result> LeftJoin<TOuter, TInner, TKey, Result>(
  this IEnumerable<TOuter> outer, IEnumerable<TInner> inner
  , Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector
  , Func<TOuter, TInner, Result> resultSelector, IEqualityComparer<TKey> comparer)
  {
    if (outer == null)
      throw new ArgumentException("outer");

    if (inner == null)
      throw new ArgumentException("inner");

    if (outerKeySelector == null)
      throw new ArgumentException("outerKeySelector");

    if (innerKeySelector == null)
      throw new ArgumentException("innerKeySelector");

    if (resultSelector == null)
      throw new ArgumentException("resultSelector");

    return LeftJoinImpl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer ?? EqualityComparer<TKey>.Default);
  }

  static IEnumerable<Result> LeftJoinImpl<TOuter, TInner, TKey, Result>(
      IEnumerable<TOuter> outer, IEnumerable<TInner> inner
      , Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector
      , Func<TOuter, TInner, Result> resultSelector, IEqualityComparer<TKey> comparer)
  {
    var innerLookup = inner.ToLookup(innerKeySelector, comparer);

    foreach (var outerElment in outer)
    {
      var outerKey = outerKeySelector(outerElment);
      var innerElements = innerLookup[outerKey];

      if (innerElements.Any())
        foreach (var innerElement in innerElements)
          yield return resultSelector(outerElment, innerElement);
      else
        yield return resultSelector(outerElment, default(TInner));
     }
   }

ตัวเลือก resultsor นั้นจะต้องดูแลอิลิเมนต์ null Fx

   static void Main(string[] args)
   {
     var inner = new[] { Tuple.Create(1, "1"), Tuple.Create(2, "2"), Tuple.Create(3, "3") };
     var outer = new[] { Tuple.Create(1, "11"), Tuple.Create(2, "22") };

     var res = outer.LeftJoin(inner, item => item.Item1, item => item.Item1, (it1, it2) =>
     new { Key = it1.Item1, V1 = it1.Item2, V2 = it2 != null ? it2.Item2 : default(string) });

     foreach (var item in res)
       Console.WriteLine(string.Format("{0}, {1}, {2}", item.Key, item.V1, item.V2));
   }

4
นี่เป็นเพียงตัวเลือกสำหรับ LINQ ไปยังวัตถุและจะไม่สามารถแปลแบบสอบถามเป็นผู้ให้บริการแบบสอบถามใด ๆ ซึ่งเป็นกรณีการใช้งานทั่วไปสำหรับการดำเนินการนี้
Servy

13
แต่คำถามคือ "วิธีการดำเนินการด้านนอกเข้าร่วมด้านนอกใน C # LINQ กับวัตถุ ... "
เบอร์แทรนด์

12

ลองดูตัวอย่างนี้

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:

ตอนนี้คุณสามารถที่จะinclude elements from the leftแม้ว่าองค์ประกอบhas no matches in the rightนั้นในกรณีของเราที่เราได้รับArleneแม้ว่าเขาจะไม่มีการจับคู่ในด้านขวา

นี่คือการอ้างอิง

วิธีการ: ดำเนินการเข้าร่วม Left Left (คู่มือการเขียนโปรแกรม C #)


ผลลัพธ์ควรเป็น: Arlene: ไม่มีอยู่
user1169587

10

นี่คือรูปแบบทั่วไป (ตามที่ระบุไว้แล้วในคำตอบอื่น ๆ )

var c =
    from a in alpha
    join b in beta on b.field1 equals a.field1 into b_temp
    from b_value in b_temp.DefaultIfEmpty()
    select new { Alpha = a, Beta = b_value };

อย่างไรก็ตามนี่คือคำอธิบายที่ฉันหวังว่าจะอธิบายความหมายของสิ่งนี้จริง ๆ !

join b in beta on b.field1 equals a.field1 into b_temp

เป็นหลักสร้างชุดผลลัพธ์แยกต่างหาก b_temp ที่มี 'แถว' เป็นโมฆะสำหรับรายการทางด้านขวามือ (รายการใน 'b')

จากนั้นบรรทัดถัดไป:

from b_value in b_temp.DefaultIfEmpty()

.. กำหนดชุดผลลัพธ์นั้นการตั้งค่าเริ่มต้นเป็นโมฆะสำหรับ 'แถว' ทางด้านขวามือและตั้งค่าผลลัพธ์ของแถวด้านขวามือเข้าร่วมกับค่า 'b_value' (เช่นค่าที่อยู่ทางขวา ด้านข้างมือหากมีระเบียนที่ตรงกันหรือ 'null' หากไม่มี)

ตอนนี้ถ้าด้านขวาเป็นผลลัพธ์ของการสืบค้น LINQ แยกต่างหากมันจะประกอบด้วยประเภทที่ไม่ระบุตัวตนซึ่งอาจเป็น 'บางสิ่ง' หรือ 'null' เท่านั้น หากเป็นจำนวนเต็ม (เช่นรายการ - โดยที่ MyObjectB เป็นคลาสที่มี 2 ฟิลด์) แสดงว่าเป็นไปได้ที่เฉพาะเจาะจงเกี่ยวกับค่า 'null' ที่เป็นค่าเริ่มต้นสำหรับคุณสมบัติของมัน:

var c =
    from a in alpha
    join b in beta on b.field1 equals a.field1 into b_temp
    from b_value in b_temp.DefaultIfEmpty( new MyObjectB { Field1 = String.Empty, Field2 = (DateTime?) null })
    select new { Alpha = a, Beta_field1 = b_value.Field1, Beta_field2 = b_value.Field2 };

สิ่งนี้ทำให้มั่นใจได้ว่า 'b' ไม่ได้เป็นโมฆะ (แต่คุณสมบัติสามารถเป็นโมฆะได้โดยใช้ค่าเริ่มต้นที่คุณระบุไว้) และสิ่งนี้ช่วยให้คุณสามารถตรวจสอบคุณสมบัติของ b_value ได้โดยไม่ได้รับข้อยกเว้นการอ้างอิงว่างสำหรับ b_value โปรดทราบว่าสำหรับ DateTime ที่เป็นโมฆะจะต้องระบุประเภทของ 'DateTime?' เช่น 'Dateable nullTime' เป็น 'Type' ของ null ในข้อมูลจำเพาะสำหรับ 'DefaultIfEmpty' (ซึ่งจะใช้กับประเภทที่ไม่ใช่ 'ดั้งเดิมด้วย) 'nullable เช่น double, float)

คุณสามารถทำการรวมภายนอกด้านซ้ายหลายรายการโดยเพียงแค่ผูกมัดไวยากรณ์ข้างต้น


1
b_value มาจากไหน
Jack Fraser

9

นี่คือตัวอย่างถ้าคุณต้องการเข้าร่วมมากกว่า 2 ตาราง:

from d in context.dc_tpatient_bookingd
join bookingm in context.dc_tpatient_bookingm 
     on d.bookingid equals bookingm.bookingid into bookingmGroup
from m in bookingmGroup.DefaultIfEmpty()
join patient in dc_tpatient
     on m.prid equals patient.prid into patientGroup
from p in patientGroup.DefaultIfEmpty()

Ref: https://stackoverflow.com/a/17142392/2343


4

วิธีการขยายที่ทำงานเหมือนเข้าร่วมซ้ายกับเข้าร่วมไวยากรณ์

public static class LinQExtensions
{
    public static IEnumerable<TResult> LeftJoin<TOuter, TInner, TKey, TResult>(
        this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, 
        Func<TOuter, TKey> outerKeySelector, 
        Func<TInner, TKey> innerKeySelector, 
        Func<TOuter, TInner, TResult> resultSelector)
    {
        return outer.GroupJoin(
            inner, 
            outerKeySelector, 
            innerKeySelector,
            (outerElement, innerElements) => resultSelector(outerElement, innerElements.FirstOrDefault()));
    }
}

เพิ่งเขียนมันใน. NET core และดูเหมือนว่าจะทำงานได้ตามที่คาดไว้

ทดสอบเล็กน้อย:

        var Ids = new List<int> { 1, 2, 3, 4};
        var items = new List<Tuple<int, string>>
        {
            new Tuple<int, string>(1,"a"),
            new Tuple<int, string>(2,"b"),
            new Tuple<int, string>(4,"d"),
            new Tuple<int, string>(5,"e"),
        };

        var result = Ids.LeftJoin(
            items,
            id => id,
            item => item.Item1,
            (id, item) => item ?? new Tuple<int, string>(id, "not found"));

        result.ToList()
        Count = 4
        [0]: {(1, a)}
        [1]: {(2, b)}
        [2]: {(3, not found)}
        [3]: {(4, d)}

4

นี่เป็นเวอร์ชันที่เข้าใจง่ายโดยใช้ไวยากรณ์เมธอด:

IEnumerable<JoinPair> outerLeft =
    lefts.SelectMany(l => 
        rights.Where(r => l.Key == r.Key)
              .DefaultIfEmpty(new Item())
              .Select(r => new JoinPair { LeftId = l.Id, RightId = r.Id }));

3

มีสามตารางคือบุคคลโรงเรียนและ person_schools ซึ่งเชื่อมโยงบุคคลกับโรงเรียนที่พวกเขาเรียนมาอ้างอิงถึงบุคคลที่มี id = 6 อยู่ในตาราง person_schools อย่างไรก็ตามบุคคลที่มี id = 6 จะแสดงในกริดรวมผลลัพธ์ lef

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

ในขณะนี้อาจจะเป็นคำตอบของคำถามให้คำอธิบายเกี่ยวกับคำตอบของคุณ :)
อาเมียร์

2

นี่คือไวยากรณ์ SQL เปรียบเทียบกับไวยากรณ์ LINQ สำหรับการรวมภายนอกด้านในและด้านซ้าย ด้านนอกซ้ายเข้าร่วม:

http://www.ozkary.com/2011/07/linq-to-entity-inner-and-left-joins.html

"ตัวอย่างต่อไปนี้จะรวมกลุ่มระหว่างผลิตภัณฑ์และหมวดหมู่นี่คือการเข้าร่วมด้านซ้ายการเข้าสู่นิพจน์ส่งคืนข้อมูลแม้ว่าตารางหมวดหมู่จะว่างเปล่าหากต้องการเข้าถึงคุณสมบัติของตารางหมวดหมู่ตอนนี้เราต้องเลือกจากผลลัพธ์ที่นับได้ โดยเพิ่มคำสั่งจาก cl ใน catList.DefaultIfEmpty ()


1

ดำเนินการรวมภายนอกด้านซ้ายใน linq C # // ดำเนินการรวมภายนอกด้านซ้าย

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/


1

ฉันต้องการเพิ่มว่าถ้าคุณได้รับส่วนขยาย MoreLinq ตอนนี้มีการสนับสนุนทั้งซ้ายและเป็นเนื้อเดียวกัน heterogeneous ซ้ายเข้าร่วมตอนนี้

http://morelinq.github.io/2.8/ref/api/html/Overload_MoreLinq_MoreEnumerable_LeftJoin.htm

ตัวอย่าง:

//Pretend a ClientCompany object and an Employee object both have a ClientCompanyID key on them

return DataContext.ClientCompany
    .LeftJoin(DataContext.Employees,                         //Table being joined
        company => company.ClientCompanyID,                  //First key
        employee => employee.ClientCompanyID,                //Second Key
        company => new {company, employee = (Employee)null}, //Result selector when there isn't a match
        (company, employee) => new { company, employee });   //Result selector when there is a match

แก้ไข:

ในการหวนกลับนี้อาจทำงานได้ แต่จะแปลง IQueryable เป็น IEnumerable เป็น morelinq ไม่แปลงแบบสอบถามเป็น SQL

คุณสามารถใช้ GroupJoin แทนได้ตามที่อธิบายไว้ที่นี่: https://stackoverflow.com/a/24273804/4251433

สิ่งนี้จะช่วยให้มั่นใจได้ว่ามันจะยังคงอยู่ในระดับ IQueryable ในกรณีที่คุณจำเป็นต้องดำเนินการทางตรรกะเพิ่มเติมในภายหลัง


1

วิธีง่ายๆคือใช้คำหลักให้ มันใช้งานได้สำหรับฉัน

from AItem in Db.A
Let BItem = Db.B.Where(x => x.id == AItem.id ).FirstOrDefault() 
Where SomeCondition
Select new YourViewModel
{
    X1 = AItem.a,
    X2 = AItem.b,
    X3 = BItem.c
}

นี่คือการจำลอง Left Left ถ้าแต่ละรายการในตาราง B ไม่ตรงกับ A รายการ BItem จะคืนค่าว่าง


0

หากคุณต้องการเข้าร่วมและกรองสิ่งที่สามารถทำได้นอกการเข้าร่วม ตัวกรองสามารถทำได้หลังจากสร้างคอลเลกชัน

ในกรณีนี้ถ้าฉันทำเช่นนี้ในเงื่อนไขการเข้าร่วมฉันจะลดแถวที่ส่งคืน

ใช้เงื่อนไขแบบไตรภาค (= n == null ? "__" : n.MonDayNote,)

  • ถ้าวัตถุอยู่null(เพื่อไม่ตรง) ?แล้วกลับสิ่งที่อยู่หลัง __, ในกรณีนี้.

  • อื่นกลับสิ่งที่อยู่หลัง,:n.MonDayNote

ขอบคุณผู้มีส่วนร่วมคนอื่น ๆ ที่ฉันเริ่มต้นด้วยปัญหาของฉันเอง


        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
class Program
{
    List<Employee> listOfEmp = new List<Employee>();
    List<Department> listOfDepart = new List<Department>();

    public Program()
    {
        listOfDepart = new List<Department>(){
            new Department { Id = 1, DeptName = "DEV" },
            new Department { Id = 2, DeptName = "QA" },
            new Department { Id = 3, DeptName = "BUILD" },
            new Department { Id = 4, DeptName = "SIT" }
        };


        listOfEmp = new List<Employee>(){
            new Employee { Empid = 1, Name = "Manikandan",DepartmentId=1 },
            new Employee { Empid = 2, Name = "Manoj" ,DepartmentId=1},
            new Employee { Empid = 3, Name = "Yokesh" ,DepartmentId=0},
            new Employee { Empid = 3, Name = "Purusotham",DepartmentId=0}
        };

    }
    static void Main(string[] args)
    {
        Program ob = new Program();
        ob.LeftJoin();
        Console.ReadLine();
    }

    private void LeftJoin()
    {
        listOfEmp.GroupJoin(listOfDepart.DefaultIfEmpty(), x => x.DepartmentId, y => y.Id, (x, y) => new { EmpId = x.Empid, EmpName = x.Name, Dpt = y.FirstOrDefault() != null ? y.FirstOrDefault().DeptName : null }).ToList().ForEach
            (z =>
            {
                Console.WriteLine("Empid:{0} EmpName:{1} Dept:{2}", z.EmpId, z.EmpName, z.Dpt);
            });
    }
}

class Employee
{
    public int Empid { get; set; }
    public string Name { get; set; }
    public int DepartmentId { get; set; }
}

class Department
{
    public int Id { get; set; }
    public string DeptName { get; set; }
}

เอาท์พุท


0

ตามคำตอบของฉันสำหรับคำถามที่คล้ายกันที่นี่:

Linq ไปยัง SQL ด้านนอกเข้าร่วมโดยใช้ไวยากรณ์แลมบ์ดาและเข้าร่วมใน 2 คอลัมน์ (คีย์การรวมแบบรวม)

รับรหัสที่นี่หรือคัดลอกrepit GitHub ของฉันและเล่น!

ค้นหา:

        var petOwners =
            from person in People
            join pet in Pets
            on new
            {
                person.Id,
                person.Age,
            }
            equals new
            {
                pet.Id,
                Age = pet.Age * 2, // owner is twice age of pet
            }
            into pets
            from pet in pets.DefaultIfEmpty()
            select new PetOwner
            {
                Person = person,
                Pet = pet,
            };

แลมบ์ดา:

        var petOwners = People.GroupJoin(
            Pets,
            person => new { person.Id, person.Age },
            pet => new { pet.Id, Age = pet.Age * 2 },
            (person, pet) => new
            {
                Person = person,
                Pets = pet,
            }).SelectMany(
            pet => pet.Pets.DefaultIfEmpty(),
            (people, pet) => new
            {
                people.Person,
                Pet = pet,
            });

0

ภาพรวม: ในตัวอย่างโค้ดนี้ฉันสาธิตวิธีจัดกลุ่มตาม ID โดยที่ Table1 และ Table2 มีความสัมพันธ์แบบหนึ่งต่อหลาย ฉันจัดกลุ่มที่ Id, Field1 และ Field2 แบบสอบถามย่อยมีประโยชน์หากต้องการค้นหาตารางที่สามและจะต้องมีความสัมพันธ์เข้าร่วมด้านซ้าย ฉันแสดงกลุ่มเข้าร่วมด้านซ้ายและคิวรีย่อยของคิวรี ผลลัพธ์ที่ได้จะเทียบเท่า

class MyView
{
public integer Id {get,set};
    public String Field1  {get;set;}
public String Field2 {get;set;}
    public String SubQueryName {get;set;}                           
}

IList<MyView> list = await (from ci in _dbContext.Table1
                                               join cii in _dbContext.Table2
                                                   on ci.Id equals cii.Id

                                               where ci.Field1 == criterion
                                               group new
                                               {
                                                   ci.Id
                                               } by new { ci.Id, cii.Field1, ci.Field2}

                                           into pg
                                               select new MyView
                                               {
                                                   Id = pg.Key.Id,
                                                   Field1 = pg.Key.Field1,
                                                   Field2 = pg.Key.Field2,
                                                   SubQueryName=
                                                   (from chv in _dbContext.Table3 where chv.Id==pg.Key.Id select chv.Field1).FirstOrDefault()
                                               }).ToListAsync<MyView>();


 Compared to using a Left Join and Group new

IList<MyView> list = await (from ci in _dbContext.Table1
                                               join cii in _dbContext.Table2
                                                   on ci.Id equals cii.Id

                       join chv in _dbContext.Table3
                                                  on cii.Id equals chv.Id into lf_chv
                                                from chv in lf_chv.DefaultIfEmpty()

                                               where ci.Field1 == criterion
                                               group new
                                               {
                                                   ci.Id
                                               } by new { ci.Id, cii.Field1, ci.Field2, chv.FieldValue}

                                           into pg
                                               select new MyView
                                               {
                                                   Id = pg.Key.Id,
                                                   Field1 = pg.Key.Field1,
                                                   Field2 = pg.Key.Field2,
                                                   SubQueryName=pg.Key.FieldValue
                                               }).ToListAsync<MyView>();

-1
(from a in db.Assignments
     join b in db.Deliveryboys on a.AssignTo equals b.EmployeeId  

     //from d in eGroup.DefaultIfEmpty()
     join  c in  db.Deliveryboys on a.DeliverTo equals c.EmployeeId into eGroup2
     from e in eGroup2.DefaultIfEmpty()
     where (a.Collected == false)
     select new
     {
         OrderId = a.OrderId,
         DeliveryBoyID = a.AssignTo,
         AssignedBoyName = b.Name,
         Assigndate = a.Assigndate,
         Collected = a.Collected,
         CollectedDate = a.CollectedDate,
         CollectionBagNo = a.CollectionBagNo,
         DeliverTo = e == null ? "Null" : e.Name,
         DeliverDate = a.DeliverDate,
         DeliverBagNo = a.DeliverBagNo,
         Delivered = a.Delivered

     });

-1

ทางออกที่ง่ายสำหรับการเข้าร่วมด้านซ้าย :

var setA = context.SetA;
var setB = context.SetB.Select(st=>st.Id).Distinct().ToList();
var leftOuter  = setA.Where(stA=> !setB.Contains(stA.Id)); 

หมายเหตุ :

  • เพื่อปรับปรุงประสิทธิภาพ SetB สามารถแปลงเป็นพจนานุกรม (ถ้าทำแล้วคุณต้องเปลี่ยนสิ่งนี้: ! setB.Contains (stA.Id) ) หรือHashSet
  • เมื่อมีมากกว่าหนึ่งฟิลด์ที่เกี่ยวข้องสามารถทำได้โดยใช้Set actionsและคลาสที่ใช้: IEqualityComparer

การเข้าร่วมด้านนอกด้านซ้ายจะคืนการจับคู่setAและsetBในคำตอบ
NetMage
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.