c# LEFT OUTER JOIN في LINQ




(12)

Necromancing.
إذا تم استخدام قاعدة بيانات موفر 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()

بناء الجملة هذا مربك للغاية ، وليس من الواضح كيف يعمل عندما تريد أن تنضم إلى جداول متعددة.

ملحوظة
وتجدر الإشارة إلى أن from alias in Repo.whatever.Where(condition).DefaultIfEmpty() هو نفس خارجي تطبيق / يسار - الانضمام إلى جانبي ، أي أي محسّن قاعدة البيانات (غير المتخلفين) قادر تماما على الترجمة في ارتباط يسرى ، طالما أنك لا تقدم قيمًا لكل صف (ويعرف أيضًا بتطبيق خارجي فعلي). لا تفعل هذا في Linq-2-Objects (لأنه لا يوجد محسّن DB عند استخدام 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
    }

);

https://code.i-harness.com

كيفية إجراء صلة خارجية اليسار في 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; }

أداء الصلات الخارجية اليسرى في 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/


إذا كنت بحاجة إلى الانضمام وتصفية شيء ما ، فيمكن القيام بذلك خارج الصلة. يمكن أن يتم تصفية بعد إنشاء المجموعة.

في هذه الحالة إذا قمت بذلك في حالة الانضمام قلل الصفوف التي يتم إرجاعها.

الشرط الثلاثي يستخدم (= n == null ? "__" : n.MonDayNote,)

  • إذا كان الكائن null (لذلك لا يوجد تطابق) ، فأعد ما بعده ? . __ ، في هذه الحالة.

  • عدا ذلك ، n.MonDayNote ما : ، 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);

إلقاء نظرة على هذا المثال

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

هنا هو المرجع

كيفية القيام بما يلي: إجراء عمليات الربط الخارجي (دليل برمجة C #)


الآن كطريقة تمديد:

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

آمل أن هذا يوفر عليك بعض الوقت.


الق نظرة على هذا example . يجب أن يعمل هذا الاستعلام:

var leftFinal =
        from l in lefts
        join r in rights on l equals r.Left into lrs
        from lr in lrs.DefaultIfEmpty()
        select new { LeftId = l.Id, RightId = ((l.Key==r.Key) ? r.Id : 0 };

حل بسيط ل LEFT OUTER JOIN :

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 والفئة التي تنفذ: IEqualityComparer

طريقة الإضافة التي تعمل مثل الانضمام إلى اليسار مع صيغة انضمام

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 ويبدو أن تعمل كما هو متوقع.

اختبار صغير:

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

هذا بناء جملة SQL مقارنة بناء جملة LINQ من أجل الصلات الخارجية الداخلية واليسرى. ترك صلة خارجية:

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

"المثال التالي ينضم إلى مجموعة ما بين المنتج والفئة. هذا هو أساسًا الارتباط الأيسر. في التعبير ، ترجع البيانات حتى إذا كان جدول الفئات فارغًا. للوصول إلى خصائص جدول الفئات ، يجب أن نختار الآن من النتائج التي لا تعد ولا تحصى. بإضافة من cl في عبارة catList.DefaultIfEmpty ().


هذا هو الشكل العام (كما هو مذكور بالفعل في إجابات أخرى)

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

..iter على مجموعة النتائج هذه ، مع تعيين القيمة الافتراضية للخانة 'row' على الجانب الأيسر ، وتعيين نتيجة الصف الجانبي الأيمن إلى قيمة 'b_value' (أي القيمة الموجودة على اليمين جنبًا إلى جنب ، إذا كان هناك سجل مطابق ، أو "فارغ" إذا لم يكن هناك سجل مطابق).

الآن ، إذا كان الجانب الأيمن هو نتيجة استعلام LINQ منفصل ، فسوف يتكون من أنواع مجهولة ، والتي يمكن أن تكون إما "شيء" أو "خالية". إذا كانت قابلة للتعداد (على سبيل المثال ، قائمة - حيث MyObjectB هي فئة بها حقلين) ، فمن الممكن أن تكون محددة حول القيم الافتراضية "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؟) أي 'DateTime nullable' على أنه 'Type' of the null في المواصفة لـ 'DefaultIfEmpty' (سوف ينطبق هذا أيضًا على الأنواع التي ليست 'natively "على سبيل المثال nullable مزدوجة ، تعويم".

يمكنك تنفيذ العديد من الصلات الخارجية اليسرى ببساطة عن طريق ربط بناء الجملة أعلاه.


يمكن أن يبدو تنفيذ الربط الخارجي الأيسر بواسطة أساليب ملحق

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

بعد ذلك ، يجب على وحدة النتائج أن تعتني بالعناصر الفارغة. الفوركس.

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

(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

     });






join