LEFT OUTER JOIN in LINQ


539

Wie führe ich eine linke äußere Verknüpfung in C # LINQ mit Objekten ohne Verwendung von join-on-equals-intoKlauseln durch? Gibt es eine Möglichkeit, dies mit whereKlausel zu tun ? Richtiges Problem: Die innere Verbindung ist einfach und ich habe eine Lösung wie diese

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

aber für die linke äußere Verbindung brauche ich eine Lösung. Meins ist so etwas, aber es funktioniert nicht

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

wo JoinPair eine Klasse ist:

public class JoinPair { long leftId; long rightId; }

2
Können Sie ein Beispiel geben, was Sie erreichen wollen?
Jeroenh

Der normale linke äußere Join ist ungefähr so: var a = von b in bb join c in cc auf b.bbbbb entspricht c.ccccc in dd von d in dd.DefaultIfEmpty () select b.sss; Meine Frage ist, ob es eine Möglichkeit gibt, dies mit Join-on-Equals-In-Klauseln wie dieser zu tun. Var a = von b in bb von c in cc wobei b.bbb == c.cccc ... und so weiter. .
Toy

1
Sicher gibt es das, aber Sie sollten ein Beispiel für Ihren Code veröffentlichen, den Sie bereits haben, damit die Leute Ihnen eine bessere Antwort geben können
Faultier

Ich suchte nach einem "Links ohne " JOIN (und verwechselte es mit dem Konzept von "OUTER"). Diese Antwort war näher an dem, was ich wollte.
Die rote Erbse

In Verbindung stehender Beitrag - Linq Join iquery, wie man defaultifempty verwendet
RBT

Antworten:


598

Wie angegeben auf:

101 LINQ-Beispiele - Linke äußere Verbindung

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
Ich versuche das Gleiche, erhalte jedoch einen Fehler beim Join-Operator, der besagt: "Der Typ eines der Ausdrücke in der Join-Klausel ist falsch."
Badhon Jain

3
@jain Wenn Ihre Typen unterschiedlich sind, funktioniert der Join nicht. Wahrscheinlich haben Ihre Schlüssel unterschiedliche Datentypen. Sind beide Schlüssel zum Beispiel int?
Yooakim

2
Was ist die Lösung Jain? Ich habe auch den gleichen Fehler und die Typen sind auch in meinem Fall gleich.
Sandeep

1
@Sandeep Überprüfen Sie Ihre Schlüssel, wo Sie es verbunden haben. Angenommen, wenn diese vom Typ string und int sind, konvertieren Sie einfach den String-Schlüssel in int.
Ankit


546

Wenn ein datenbankgesteuerter LINQ-Anbieter verwendet wird, kann ein wesentlich besser lesbarer linker Outer Join als solcher geschrieben werden:

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

Wenn Sie das weglassen DefaultIfEmpty(), haben Sie eine innere Verbindung.

Nehmen Sie die akzeptierte Antwort:

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

Diese Syntax ist sehr verwirrend und es ist nicht klar, wie sie funktioniert, wenn Sie MULTIPLE-Tabellen links verknüpfen möchten.

Hinweis
Es sollte beachtet werden, dass dies from alias in Repo.whatever.Where(condition).DefaultIfEmpty()dasselbe ist wie ein Outer-Apply / Left-Join-Lateral, das jeder (anständige) Datenbankoptimierer perfekt in einen Left-Join übersetzen kann, solange Sie nicht pro Zeile einführen -Werte (auch bekannt als tatsächliche äußere Anwendung). Tun Sie dies nicht in Linq-2-Objects (da es keinen DB-Optimierer gibt, wenn Sie Linq-to-Objects verwenden).

Detailliertes Beispiel

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

Bei Verwendung mit LINQ 2 SQL lässt sich dies gut in die folgende gut lesbare SQL-Abfrage übersetzen:

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

Bearbeiten:

Ein komplexeres Beispiel finden Sie unter " Konvertieren der SQL Server-Abfrage in eine Linq-Abfrage ".

Wenn Sie dies in Linq-2-Objects (anstelle von Linq-2-SQL) tun, sollten Sie dies auf die altmodische Weise tun (da LINQ to SQL dies korrekt übersetzt, um Operationen zu verbinden, jedoch über Objekte dieser Methode erzwingt einen vollständigen Scan und nutzt die Indexsuche nicht aus, warum auch immer ...):

    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
Diese Antwort ist tatsächlich hilfreich. Vielen Dank, dass Sie tatsächlich eine verständliche Syntax anbieten.
Chris Marisic

3
WTB eine NHibernate-kompatible LINQ-Abfrage ... :)
mxmissile

30
LINQ to SQL übersetzt dies korrekt, um Operationen zu verbinden. Bei Objekten erzwingt diese Methode jedoch einen vollständigen Scan. Aus diesem Grund bietet die offizielle Dokumentation die Gruppenverbindungslösung, die Hashes für die Indexsuche nutzen kann.
Tamir Daniely

3
Ich denke, die Syntax von explizit joinist viel lesbarer und klarer als eine wheregefolgt vonDefaultIfEmpty
FindOut_Quran

1
@ user3441905: Solange Sie nur Tabelle a mit Tabelle b verbinden müssen, kann dies sein. Aber sobald Sie mehr als das haben, wird es nicht sein. Aber selbst für nur 2 Tische finde ich es zu ausführlich. Die Meinung der Bevölkerung scheint auch gegen Sie zu sein, da diese Antwort mit 0 begann, als die Top-Antwort bereits über 90 positive Stimmen hatte.
Stefan Steiger

132

Verwenden des Lambda-Ausdrucks

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
Sowohl Join als auch GroupJoin unterstützen Left-Join nicht wirklich. Der Trick bei der Verwendung von GroupJoin besteht darin, dass Sie leere Gruppen haben und diese leeren Gruppen dann in leere Werte übersetzen können. DefaultIfEmpty macht das einfach, was bedeutet, dass Enumerable.Empty<Product>.DefaultIfEmpty()eine IEnumerable mit einem einzelnen Wert von zurückgegeben wird default(Product).
Tamir Daniely

61
All dies, um einen linken Join durchzuführen?
FindOut_Quran

7
Danke dafür! Nicht zu viele Lambda-Ausdrucksbeispiele da draußen, das hat bei mir funktioniert.
Johan Henkens

1
Danke für die Antwort. Es ergab das, was dem über die Jahre geschriebenen SQL LEFT OUTER JOIN am nächsten kam
John Gathogo

1
Benötigen Sie nicht wirklich das letzte Select (), das anon obj in SelectMany () kann für dieselbe Ausgabe überarbeitet werden. Ein anderer Gedanke ist, y auf Null zu testen, um eine engere LEFT JOIN-Äquivalenz zu simulieren.
Denny Jacob

46

Jetzt als Erweiterungsmethode:

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

Verwenden Sie, wie Sie normalerweise join verwenden würden:

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

Hoffe das spart dir etwas Zeit.


44

Schauen Sie sich dieses Beispiel an . Diese Abfrage sollte funktionieren:

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
Kann rin der select-Klausel nach Verwendung eines Joins auf zugegriffen werden?
Farhad Alizadeh Noori

@FarhadAlizadehNoori Ja, das kann es.
Po-ta-toe

Der Autor wollte wahrscheinlich rin der zweiten fromKlausel wiederverwenden . dh from r in lrs.DefaultIfEmpty()Andernfalls ist diese Abfrage nicht sehr sinnvoll und wird wahrscheinlich nicht kompiliert r, da der Kontext für die Auswahl nicht stimmt .
Saeb Amini

@ Devart, als ich Ihre Anfrage las, erinnerte sie mich an einen Film Clockwisemit John Cleese, lol.
Matas Vaitkevicius

1
Von links nach rechts in links rechts in rechts links in leftRights ... Oh je ... Die Syntax der Verwendung von LEFT OUTER JOIN in LINQ ist wirklich nicht klar, aber diese Namen machen es noch unklarer.
Mike Gledhill

19

Eine Implementierung von Left Outer Join durch Erweiterungsmethoden könnte so aussehen

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

Die Ergebnisauswahl muss sich dann um die Nullelemente kümmern. 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
Dies ist jedoch nur eine Option für LINQ für Objekte und kann die Abfrage nicht an Abfrageanbieter übersetzen. Dies ist der häufigste Anwendungsfall für diesen Vorgang.
Servy

13
Aber die Frage war "Wie man eine linke äußere Verknüpfung in C # LINQ zu Objekten durchführt ..."
Bertrand

12

Schauen Sie sich dieses Beispiel an

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:

Jetzt können Sie include elements from the leftauch wenn dieses Element has no matches in the right, in unserem Fall haben wir zurückgerufenArlene wenn er keine Übereinstimmung im Recht hat

Hier ist die Referenz

Gewusst wie: Ausführen linker äußerer Verknüpfungen (C # -Programmierhandbuch)


Die Ausgabe sollte sein: Arlene: Existiert nicht
user1169587

10

Dies ist die allgemeine Form (wie bereits in anderen Antworten angegeben)

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

Hier ist jedoch eine Erklärung, von der ich hoffe, dass sie klarstellt, was dies tatsächlich bedeutet!

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

erstellt im Wesentlichen eine separate Ergebnismenge b_temp, die effektiv Null-Zeilen für Einträge auf der rechten Seite enthält (Einträge in 'b').

Dann die nächste Zeile:

from b_value in b_temp.DefaultIfEmpty()

..iteriert über diese Ergebnismenge, setzt den Standard-Nullwert für die 'Zeile' auf der rechten Seite und setzt das Ergebnis der Zeilenverknüpfung auf der rechten Seite auf den Wert von 'b_value' (dh den Wert auf der rechten Seite) Seite, wenn es einen passenden Datensatz gibt, oder 'null', wenn es keinen gibt).

Wenn die rechte Seite das Ergebnis einer separaten LINQ-Abfrage ist, besteht sie aus anonymen Typen, die nur entweder "etwas" oder "null" sein können. Wenn es sich jedoch um eine Aufzählung handelt (z. B. eine Liste - wobei MyObjectB eine Klasse mit 2 Feldern ist), können Sie genau angeben, welche Standardwerte für die Eigenschaften verwendet werden sollen:

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

Dadurch wird sichergestellt, dass 'b' selbst nicht null ist (seine Eigenschaften können jedoch unter Verwendung der von Ihnen angegebenen Standard-Nullwerte null sein). Auf diese Weise können Sie die Eigenschaften von b_value überprüfen, ohne eine Nullreferenzausnahme für b_value zu erhalten. Beachten Sie, dass für eine nullfähige DateTime ein Typ von (DateTime?), Dh 'nullable DateTime', als 'Typ' der Null in der Spezifikation für 'DefaultIfEmpty' angegeben werden muss (dies gilt auch für Typen, die nicht 'nativ' sind 'nullable zB double, float).

Sie können mehrere linke äußere Verknüpfungen ausführen, indem Sie einfach die obige Syntax verketten.


1
Woher kommt b_value?
Jack Fraser

9

Hier ist ein Beispiel, wenn Sie mehr als 2 Tabellen verbinden müssen:

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

Erweiterungsmethode, die wie Left Join mit Join-Syntax funktioniert

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

habe es gerade in .NET Core geschrieben und es scheint wie erwartet zu funktionieren.

Kleiner Test:

        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

Hier ist eine ziemlich einfach zu verstehende Version mit Methodensyntax:

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

Es gibt drei Tabellen: Personen, Schulen und Personenschulen, die Personen mit den Schulen verbinden, in denen sie studieren. In der Tabelle person_schools fehlt ein Verweis auf die Person mit id = 6. Die Person mit id = 6 wird jedoch im Ergebnis-Lef-Join-Raster dargestellt.

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

Während dies vielleicht die Antwort auf die Frage ist, geben Sie eine Erklärung für Ihre Antwort :)
Amir

2

Dies ist eine SQL-Syntax im Vergleich zur LINQ-Syntax für innere und linke äußere Verknüpfungen. Linke äußere Verbindung:

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

"Im folgenden Beispiel wird eine Gruppenverknüpfung zwischen Produkt und Kategorie durchgeführt. Dies ist im Wesentlichen die linke Verknüpfung. Der Ausdruck into gibt Daten zurück, auch wenn die Kategorietabelle leer ist. Um auf die Eigenschaften der Kategorietabelle zugreifen zu können, müssen wir jetzt aus dem aufzählbaren Ergebnis auswählen durch Hinzufügen der from cl-Anweisung in catList.DefaultIfEmpty ().


1

Führen Sie linke äußere Verknüpfungen in linq C # aus. // Führen Sie linke äußere Verknüpfungen durch

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

Ich möchte hinzufügen, dass wenn Sie die MoreLinq-Erweiterung erhalten, jetzt sowohl homogene als auch heterogene Linksverknüpfungen unterstützt werden

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

Beispiel:

//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

BEARBEITEN:

Im Nachhinein mag dies funktionieren, aber es konvertiert die IQueryable in eine IEnumerable, da morelinq die Abfrage nicht in SQL konvertiert.

Sie können stattdessen einen GroupJoin wie hier beschrieben verwenden: https://stackoverflow.com/a/24273804/4251433

Dadurch wird sichergestellt, dass es als IQueryable verbleibt, falls Sie später weitere logische Operationen ausführen müssen.


1

Der einfache Weg ist die Verwendung des Schlüsselworts Let. Das funktioniert bei mir.

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
}

Dies ist eine Simulation von Left Join. Wenn nicht jedes Element in der Tabelle B mit dem Element A übereinstimmt, gibt BItem null zurück


0

Wenn Sie etwas beitreten und filtern müssen, kann dies außerhalb des Joins erfolgen. Der Filter kann nach dem Erstellen der Sammlung durchgeführt werden.

In diesem Fall reduziere ich die zurückgegebenen Zeilen, wenn ich dies in der Join-Bedingung mache.

Ternäre Bedingung wird verwendet (= n == null ? "__" : n.MonDayNote,)

  • Wenn das Objekt ist null(also keine Übereinstimmung), geben Sie zurück, was nach dem steht ?. __, in diesem Fall.

  • Andernfalls geben Sie zurück, was nach dem :, n.MonDayNote.

Dank der anderen Mitwirkenden habe ich dort mit meiner eigenen Ausgabe begonnen.


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

AUSGABE


0

Nach meiner Antwort auf eine ähnliche Frage hier:

Linq to SQL linker äußerer Join mit Lambda-Syntax und Joining auf 2 Spalten (zusammengesetzter Join-Schlüssel)

Holen Sie sich den Code hier oder klonen Sie mein Github-Repo und spielen Sie!

Abfrage:

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

Lambda:

        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

Übersicht: In diesem Codeausschnitt zeige ich, wie nach ID gruppiert wird, wobei Tabelle1 und Tabelle2 eine Eins-zu-Viele-Beziehung haben. Ich gruppiere auf Id, Field1 und Field2. Die Unterabfrage ist hilfreich, wenn eine dritte Tabellensuche erforderlich ist und eine Linksverknüpfungsbeziehung erforderlich gewesen wäre. Ich zeige eine Linksverknüpfungsgruppierung und eine Unterabfrage linq. Die Ergebnisse sind gleichwertig.

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

Einfache Lösung für den 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)); 

Anmerkungen :

  • Um die Leistung zu verbessern, könnte SetB in a konvertiert werden Wörterbuch (wenn dies erledigt ist, müssen Sie dies ändern : ! SetB.Contains (stA.Id) ) oder a HashSet konvertiert werden
  • Wenn mehr als ein Feld beteiligt ist, kann dies mithilfe von Set- Operationen und einer Klasse erreicht werden, die Folgendes implementiert: IEqualityComparer

Ein linker äußerer Join würde Matching setAund setBin der Antwort zurückgeben.
NetMage
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.