c# - LEFT OUTER JOIN في LINQ




(16)

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

Answers

(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

     });

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

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 مزدوجة ، تعويم".

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


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

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

الشرط الثلاثي يستخدم (= 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);

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

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

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


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


الق نظرة على هذا 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 };

هناك ثلاثة جداول: الأشخاص والمدارس والأشخاص المدربون ، تربط الأشخاص بالمدارس التي يدرسون فيها. لا توجد إشارة إلى الشخص الذي يحمل رقم الهوية = 6 في الجدول person_schools. ومع ذلك يتم تقديم الشخص مع معرف = 6 في شبكة النتيجة المرتبطة.

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

إليك مثالاً إذا كنت بحاجة إلى الانضمام إلى أكثر من جدولين:

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

المرجع: https://.com/a/17142392/2343


أود أن أضيف أنه إذا حصلت على ملحق MoreLinq ، فهناك الآن دعم لكل من الروابط المتجانسة وغير المتجانسة اليسرى الآن

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://.com/a/24273804/4251433

هذا سيضمن أن يبقى ك aqueryable في حال كنت بحاجة إلى القيام بعمليات منطقية أخرى عليه لاحقا.


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

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

كما جاء في:

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

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

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


باستخدام تعبير امدا

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

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

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

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


حل بسيط ل 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

سوف أشارك أحد المفاهيم التي أساء استخدامها أنني وقعت في يوم واحد:

var names = new List<string> {"mercedes", "mazda", "bmw", "fiat", "ferrari"};

var startingWith_M = names.Where(x => x.StartsWith("m"));

var startingWith_F = names.Where(x => x.StartsWith("f"));


// updating existing list
names[0] = "ford";

// Guess what should be printed before continuing
print( startingWith_M.ToList() );
print( startingWith_F.ToList() );

نتيجة متوقعة

// I was expecting    
print( startingWith_M.ToList() ); // mercedes, mazda
print( startingWith_F.ToList() ); // fiat, ferrari

نتيجة فعلية

// what printed actualy   
print( startingWith_M.ToList() ); // mercedes
print( startingWith_F.ToList() ); // ford, fiat, ferrari

تفسير

وفقا لإجابات أخرى ، تم تأجيل تقييم النتيجة حتى استدعاء ToList أو أساليب استدعاء مشابهة على سبيل المثال ToArray .

حتى أتمكن من إعادة كتابة الشفرة في هذه الحالة على النحو التالي:

var names = new List<string> {"mercedes", "mazda", "bmw", "fiat", "ferrari"};

// updating existing list
names[0] = "ford";

// before calling ToList directly
var startingWith_M = names.Where(x => x.StartsWith("m"));

var startingWith_F = names.Where(x => x.StartsWith("f"));

print( startingWith_M.ToList() );
print( startingWith_F.ToList() );

لعب حولها

https://repl.it/E8Ki/0





c# linq join