none
LINQ: Mehrere LEFT OUTER JOINs mit der gleichen Tabelle (DataTable) RRS feed

  • Frage

  • Hallo,

    ich habe 4 DataTables, die ich verbinden will.

    Und zwar mit mehreren Outer Joins von der gleichen Tabelle aus.

    Angenommen in Tab1 ist die äußere Tabelle, dessen Keys wollen sich nun jeweils mit den weiteren Tabellen Tab2, Tab3, Tab4 verbinden. Es soll aber auch eine Zeile erscheinen, wenn kein entsprechender Key in Tab2, Tab3, Tab4 gefunden wird. Für Tab1 soll also im Ergebnis immer was "drinstehen", und jeweils optional in Tab2/Tab3/Tab4

    in SQL ausgedrückt:

    select * from tab1

    left outer join tab2 on <verknüpfung über Key>

    left outer join tab3 on <verknüpfung über Key>

    left outer join tab4 on <verknüpfung über Key>


    Wie muß das LINQ denn dann lauten?

    Vielen Dank

    Wolfgang

    Dienstag, 20. November 2018 10:14

Antworten

  • Hi Wolfgang,
    warum reihst Du die Join nicht aneinander und speicherst das Ergebnis in einer Ergebnisklasse, z.B. so:

        class Demo
        {
          internal void Execute()
          {
            DataTable dt1 = new DataTable();
            dt1.Columns.Add("ID", typeof(int));
            dt1.Columns.Add("Wert1", typeof(string));
            for (int i = 1; i < 11; i++) dt1.Rows.Add(i, $"Wert 1 {i}");
    
            DataTable dt2 = new DataTable();
            dt2.Columns.Add("ID", typeof(int));
            dt2.Columns.Add("Wert2", typeof(string));
            for (int i = 1; i < 11; i += 2) dt2.Rows.Add(i, $"Wert 2 {i}");
    
            DataTable dt3 = new DataTable();
            dt3.Columns.Add("ID", typeof(int));
            dt3.Columns.Add("Wert3", typeof(string));
            for (int i = 1; i < 11; i += 3) dt3.Rows.Add(i, $"Wert 3 {i}");
    
            var res = from r1 in dt1.AsEnumerable()
                      join r2 in dt2.AsEnumerable()
                      on r1.Field<int>("ID") equals (int)r2["ID"] into zws1
                      from r1a in zws1.DefaultIfEmpty()
                      join r3 in dt3.AsEnumerable()
                      on r1.Field<int>("ID") equals (int)r3["ID"] into zws2
                      from r2a in zws2.DefaultIfEmpty()
                      select new C123
                      {
                        ID = r1.Field<int>("ID"),
                        Wert1 = r1.Field<string>("Wert1"),
                        Wert2 = (r1a == null) ? "<leer>" : r1a.Field<string>("Wert2"),
                        Wert3 = (r2a == null) ? "<leer>" : r2a.Field<string>("Wert3")
                      };
    
            Console.WriteLine("--- Tabelle 1");
            foreach (DataRow r1 in dt1.Rows)
              Console.WriteLine($"{r1["ID"]} - {r1["Wert1"]}");
    
            Console.WriteLine("--- Tabelle 2");
            foreach (DataRow r2 in dt2.Rows)
              Console.WriteLine($"{r2["ID"]} - {r2["Wert2"]}");
    
            Console.WriteLine("--- Tabelle 3");
            foreach (DataRow r3 in dt3.Rows)
              Console.WriteLine($"{r3["ID"]} - {r3["Wert3"]}");
    
            Console.WriteLine("--- Ergebnis");
            foreach (var item in res)
              Console.WriteLine($"{item.ID} - {item.Wert1} - {item.Wert2} - {item.Wert3}");
          }
    
          public class C123
          {
            public int ID { get; set; }
            public string Wert1 { get; set; }
            public string Wert2 { get; set; }
            public string Wert3 { get; set; }
          }
        }

    Du kannst anstelle den in einer DataTable versteckten Eigenschaften einer DataRow eigene Typen (=Klassen) nutzen, wie das auch im Entity Framework gemacht wird. In diesem Fall kannst Du in der Ergebnisklasse die Werte ablegen. Hier mal eine Demo als Anregung:

        class Demo
        {
          internal void Execute()
          {
            DataTable dt1 = new DataTable();
            dt1.Columns.Add("ID", typeof(int));
            dt1.Columns.Add("Wert1", typeof(string));
            for (int i = 1; i < 11; i++) dt1.Rows.Add(i, $"Wert 1 {i}");
    
            List<C1> l1 = new List<C1>();
            foreach (var item in dt1.AsEnumerable()) l1.Add(new C1()
            {
              ID = item.Field<int>("ID"),
              Wert1 = item.Field<string>("Wert1")
            });
    
            DataTable dt2 = new DataTable();
            dt2.Columns.Add("ID", typeof(int));
            dt2.Columns.Add("Wert2", typeof(string));
            for (int i = 1; i < 11; i += 2) dt2.Rows.Add(i, $"Wert 2 {i}");
    
            List<C2> l2 = new List<C2>();
            foreach (var item in dt2.AsEnumerable()) l2.Add(new C2()
            {
              ID = item.Field<int>("ID"),
              Wert2 = item.Field<string>("Wert2")
            });
    
            DataTable dt3 = new DataTable();
            dt3.Columns.Add("ID", typeof(int));
            dt3.Columns.Add("Wert3", typeof(string));
            for (int i = 1; i < 11; i += 3) dt3.Rows.Add(i, $"Wert 3 {i}");
    
            List<C3> l3 = new List<C3>();
            foreach (var item in dt3.AsEnumerable()) l3.Add(new C3()
            {
              ID = item.Field<int>("ID"),
              Wert3 = item.Field<string>("Wert3")
            });
    
            var res = from r1 in l1
                      join r2 in l2 on r1.ID equals r2.ID into zws1
                      from r1a in zws1.DefaultIfEmpty()
                      join r3 in l3
                      on r1.ID equals r3.ID into zws2
                      from r2a in zws2.DefaultIfEmpty()
                      select new C123
                      {
                        ID = r1.ID,
                        Wert1 = r1.Wert1,
                        Wert2 = (r1a == null) ? "<leer>" : r1a.Wert2,
                        Wert3 = (r2a == null) ? "<leer>" : r2a.Wert3
                      };
    
            Console.WriteLine("--- Tabelle 1");
            foreach (DataRow r1 in dt1.Rows)
              Console.WriteLine($"{r1["ID"]} - {r1["Wert1"]}");
    
            Console.WriteLine("--- Tabelle 2");
            foreach (DataRow r2 in dt2.Rows)
              Console.WriteLine($"{r2["ID"]} - {r2["Wert2"]}");
    
            Console.WriteLine("--- Tabelle 3");
            foreach (DataRow r3 in dt3.Rows)
              Console.WriteLine($"{r3["ID"]} - {r3["Wert3"]}");
    
            Console.WriteLine("--- Ergebnis");
            foreach (var item in res)
              Console.WriteLine($"{item.ID} - {item.Wert1} - {item.Wert2} - {item.Wert3}");
          }
    
          public class C1
          {
            public int ID { get; set; }
            public string Wert1 { get; set; }
          }
          public class C2
          {
            public int ID { get; set; }
            public string Wert2 { get; set; }
          }
    
          public class C3
          {
            public int ID { get; set; }
            public string Wert3 { get; set; }
          }
          public class C123
          {
            public int ID { get; set; }
            public string Wert1 { get; set; }
            public string Wert2 { get; set; }
            public string Wert3 { get; set; }
          }
        }

    Für 4 Tabellen mit DataTable könnte die Lösung so aussehen.

        class Demo
        {
          internal void Execute()
          {
            DataTable dt1 = new DataTable();
            dt1.Columns.Add("ID", typeof(int));
            dt1.Columns.Add("Wert1", typeof(string));
            for (int i = 1; i < 21; i++) dt1.Rows.Add(i, $"Wert 1 {i}");
    
            DataTable dt2 = new DataTable();
            dt2.Columns.Add("ID", typeof(int));
            dt2.Columns.Add("Wert2", typeof(string));
            for (int i = 1; i < 21; i += 2) dt2.Rows.Add(i, $"Wert 2 {i}");
    
            DataTable dt3 = new DataTable();
            dt3.Columns.Add("ID", typeof(int));
            dt3.Columns.Add("Wert3", typeof(string));
            for (int i = 1; i < 21; i += 3) dt3.Rows.Add(i, $"Wert 3 {i}");
    
            DataTable dt4 = new DataTable();
            dt4.Columns.Add("ID", typeof(int));
            dt4.Columns.Add("Wert4", typeof(string));
            for (int i = 1; i < 21; i += 5) dt4.Rows.Add(i, $"Wert 4 {i}");
    
            var res = from r1 in dt1.AsEnumerable()
                      join r2 in dt2.AsEnumerable()
                      on r1.Field<int>("ID") equals (int)r2["ID"] into zws1
                      from r1a in zws1.DefaultIfEmpty()
                      join r3 in dt3.AsEnumerable()
                      on r1.Field<int>("ID") equals (int)r3["ID"] into zws2
                      from r2a in zws2.DefaultIfEmpty()
                      join r4 in dt4.AsEnumerable()
                      on r1.Field<int>("ID") equals (int)r4["ID"] into zws3
                      from r3a in zws3.DefaultIfEmpty()
                      select new C1234
                      {
                        ID = r1.Field<int>("ID"),
                        Wert1 = r1.Field<string>("Wert1"),
                        Wert2 = (r1a == null) ? "<leer>" : r1a.Field<string>("Wert2"),
                        Wert3 = (r2a == null) ? "<leer>" : r2a.Field<string>("Wert3"),
                        Wert4 = (r3a == null) ? "<leer>" : r3a.Field<string>("Wert4")
                      };
    
            Console.WriteLine("--- Tabelle 1");
            foreach (DataRow r1 in dt1.Rows)
              Console.WriteLine($"{r1["ID"]} - {r1["Wert1"]}");
    
            Console.WriteLine("--- Tabelle 2");
            foreach (DataRow r2 in dt2.Rows)
              Console.WriteLine($"{r2["ID"]} - {r2["Wert2"]}");
    
            Console.WriteLine("--- Tabelle 3");
            foreach (DataRow r3 in dt3.Rows)
              Console.WriteLine($"{r3["ID"]} - {r3["Wert3"]}");
    
            Console.WriteLine("--- Ergebnis");
            foreach (var item in res)
              Console.WriteLine($"{item.ID} - {item.Wert1} - {item.Wert2} - {item.Wert3} - {item.Wert4}");
          }
    
          public class C1234
          {
            public int ID { get; set; }
            public string Wert1 { get; set; }
            public string Wert2 { get; set; }
            public string Wert3 { get; set; }
            public string Wert4 { get; set; }
          }
        }
    


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP für Developer Technologies)
    Meine Homepage mit Tipps und Tricks



    Mittwoch, 21. November 2018 08:27

Alle Antworten

  • Hallo Wolfgang,

    schau mal bitte in diesen SO Thread, da wird beschrieben, wie Du das machen kannst.

      LINQ join two DataTables


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET (2001-2018)
    https://www.asp-solutions.de/ - IT Beratung, Softwareentwicklung, Remotesupport

    Dienstag, 20. November 2018 10:21
    Moderator
  • Hallo,

    ja Danke, ich sehe aber immer nur zuhauf Beispiele mit *zwei* Tabellen. Ich will aber mehr als zwei Tabellen verbinden, und zwar so wie oben beschrieben. Ich versuche es schon geraume Zeit und bekomme nur Unsinn heraus, und ich weiss nicht woran es liegt.

    Wie kann ich wie oben beschrieben mehrere outer joins mit der gleichen Tabelle bilden?

    Dank

    Wolfgang

    Dienstag, 20. November 2018 10:46
  • Hi Wolfgang,
    warum reihst Du die Join nicht aneinander und speicherst das Ergebnis in einer Ergebnisklasse, z.B. so:

        class Demo
        {
          internal void Execute()
          {
            DataTable dt1 = new DataTable();
            dt1.Columns.Add("ID", typeof(int));
            dt1.Columns.Add("Wert1", typeof(string));
            for (int i = 1; i < 11; i++) dt1.Rows.Add(i, $"Wert 1 {i}");
    
            DataTable dt2 = new DataTable();
            dt2.Columns.Add("ID", typeof(int));
            dt2.Columns.Add("Wert2", typeof(string));
            for (int i = 1; i < 11; i += 2) dt2.Rows.Add(i, $"Wert 2 {i}");
    
            DataTable dt3 = new DataTable();
            dt3.Columns.Add("ID", typeof(int));
            dt3.Columns.Add("Wert3", typeof(string));
            for (int i = 1; i < 11; i += 3) dt3.Rows.Add(i, $"Wert 3 {i}");
    
            var res = from r1 in dt1.AsEnumerable()
                      join r2 in dt2.AsEnumerable()
                      on r1.Field<int>("ID") equals (int)r2["ID"] into zws1
                      from r1a in zws1.DefaultIfEmpty()
                      join r3 in dt3.AsEnumerable()
                      on r1.Field<int>("ID") equals (int)r3["ID"] into zws2
                      from r2a in zws2.DefaultIfEmpty()
                      select new C123
                      {
                        ID = r1.Field<int>("ID"),
                        Wert1 = r1.Field<string>("Wert1"),
                        Wert2 = (r1a == null) ? "<leer>" : r1a.Field<string>("Wert2"),
                        Wert3 = (r2a == null) ? "<leer>" : r2a.Field<string>("Wert3")
                      };
    
            Console.WriteLine("--- Tabelle 1");
            foreach (DataRow r1 in dt1.Rows)
              Console.WriteLine($"{r1["ID"]} - {r1["Wert1"]}");
    
            Console.WriteLine("--- Tabelle 2");
            foreach (DataRow r2 in dt2.Rows)
              Console.WriteLine($"{r2["ID"]} - {r2["Wert2"]}");
    
            Console.WriteLine("--- Tabelle 3");
            foreach (DataRow r3 in dt3.Rows)
              Console.WriteLine($"{r3["ID"]} - {r3["Wert3"]}");
    
            Console.WriteLine("--- Ergebnis");
            foreach (var item in res)
              Console.WriteLine($"{item.ID} - {item.Wert1} - {item.Wert2} - {item.Wert3}");
          }
    
          public class C123
          {
            public int ID { get; set; }
            public string Wert1 { get; set; }
            public string Wert2 { get; set; }
            public string Wert3 { get; set; }
          }
        }

    Du kannst anstelle den in einer DataTable versteckten Eigenschaften einer DataRow eigene Typen (=Klassen) nutzen, wie das auch im Entity Framework gemacht wird. In diesem Fall kannst Du in der Ergebnisklasse die Werte ablegen. Hier mal eine Demo als Anregung:

        class Demo
        {
          internal void Execute()
          {
            DataTable dt1 = new DataTable();
            dt1.Columns.Add("ID", typeof(int));
            dt1.Columns.Add("Wert1", typeof(string));
            for (int i = 1; i < 11; i++) dt1.Rows.Add(i, $"Wert 1 {i}");
    
            List<C1> l1 = new List<C1>();
            foreach (var item in dt1.AsEnumerable()) l1.Add(new C1()
            {
              ID = item.Field<int>("ID"),
              Wert1 = item.Field<string>("Wert1")
            });
    
            DataTable dt2 = new DataTable();
            dt2.Columns.Add("ID", typeof(int));
            dt2.Columns.Add("Wert2", typeof(string));
            for (int i = 1; i < 11; i += 2) dt2.Rows.Add(i, $"Wert 2 {i}");
    
            List<C2> l2 = new List<C2>();
            foreach (var item in dt2.AsEnumerable()) l2.Add(new C2()
            {
              ID = item.Field<int>("ID"),
              Wert2 = item.Field<string>("Wert2")
            });
    
            DataTable dt3 = new DataTable();
            dt3.Columns.Add("ID", typeof(int));
            dt3.Columns.Add("Wert3", typeof(string));
            for (int i = 1; i < 11; i += 3) dt3.Rows.Add(i, $"Wert 3 {i}");
    
            List<C3> l3 = new List<C3>();
            foreach (var item in dt3.AsEnumerable()) l3.Add(new C3()
            {
              ID = item.Field<int>("ID"),
              Wert3 = item.Field<string>("Wert3")
            });
    
            var res = from r1 in l1
                      join r2 in l2 on r1.ID equals r2.ID into zws1
                      from r1a in zws1.DefaultIfEmpty()
                      join r3 in l3
                      on r1.ID equals r3.ID into zws2
                      from r2a in zws2.DefaultIfEmpty()
                      select new C123
                      {
                        ID = r1.ID,
                        Wert1 = r1.Wert1,
                        Wert2 = (r1a == null) ? "<leer>" : r1a.Wert2,
                        Wert3 = (r2a == null) ? "<leer>" : r2a.Wert3
                      };
    
            Console.WriteLine("--- Tabelle 1");
            foreach (DataRow r1 in dt1.Rows)
              Console.WriteLine($"{r1["ID"]} - {r1["Wert1"]}");
    
            Console.WriteLine("--- Tabelle 2");
            foreach (DataRow r2 in dt2.Rows)
              Console.WriteLine($"{r2["ID"]} - {r2["Wert2"]}");
    
            Console.WriteLine("--- Tabelle 3");
            foreach (DataRow r3 in dt3.Rows)
              Console.WriteLine($"{r3["ID"]} - {r3["Wert3"]}");
    
            Console.WriteLine("--- Ergebnis");
            foreach (var item in res)
              Console.WriteLine($"{item.ID} - {item.Wert1} - {item.Wert2} - {item.Wert3}");
          }
    
          public class C1
          {
            public int ID { get; set; }
            public string Wert1 { get; set; }
          }
          public class C2
          {
            public int ID { get; set; }
            public string Wert2 { get; set; }
          }
    
          public class C3
          {
            public int ID { get; set; }
            public string Wert3 { get; set; }
          }
          public class C123
          {
            public int ID { get; set; }
            public string Wert1 { get; set; }
            public string Wert2 { get; set; }
            public string Wert3 { get; set; }
          }
        }

    Für 4 Tabellen mit DataTable könnte die Lösung so aussehen.

        class Demo
        {
          internal void Execute()
          {
            DataTable dt1 = new DataTable();
            dt1.Columns.Add("ID", typeof(int));
            dt1.Columns.Add("Wert1", typeof(string));
            for (int i = 1; i < 21; i++) dt1.Rows.Add(i, $"Wert 1 {i}");
    
            DataTable dt2 = new DataTable();
            dt2.Columns.Add("ID", typeof(int));
            dt2.Columns.Add("Wert2", typeof(string));
            for (int i = 1; i < 21; i += 2) dt2.Rows.Add(i, $"Wert 2 {i}");
    
            DataTable dt3 = new DataTable();
            dt3.Columns.Add("ID", typeof(int));
            dt3.Columns.Add("Wert3", typeof(string));
            for (int i = 1; i < 21; i += 3) dt3.Rows.Add(i, $"Wert 3 {i}");
    
            DataTable dt4 = new DataTable();
            dt4.Columns.Add("ID", typeof(int));
            dt4.Columns.Add("Wert4", typeof(string));
            for (int i = 1; i < 21; i += 5) dt4.Rows.Add(i, $"Wert 4 {i}");
    
            var res = from r1 in dt1.AsEnumerable()
                      join r2 in dt2.AsEnumerable()
                      on r1.Field<int>("ID") equals (int)r2["ID"] into zws1
                      from r1a in zws1.DefaultIfEmpty()
                      join r3 in dt3.AsEnumerable()
                      on r1.Field<int>("ID") equals (int)r3["ID"] into zws2
                      from r2a in zws2.DefaultIfEmpty()
                      join r4 in dt4.AsEnumerable()
                      on r1.Field<int>("ID") equals (int)r4["ID"] into zws3
                      from r3a in zws3.DefaultIfEmpty()
                      select new C1234
                      {
                        ID = r1.Field<int>("ID"),
                        Wert1 = r1.Field<string>("Wert1"),
                        Wert2 = (r1a == null) ? "<leer>" : r1a.Field<string>("Wert2"),
                        Wert3 = (r2a == null) ? "<leer>" : r2a.Field<string>("Wert3"),
                        Wert4 = (r3a == null) ? "<leer>" : r3a.Field<string>("Wert4")
                      };
    
            Console.WriteLine("--- Tabelle 1");
            foreach (DataRow r1 in dt1.Rows)
              Console.WriteLine($"{r1["ID"]} - {r1["Wert1"]}");
    
            Console.WriteLine("--- Tabelle 2");
            foreach (DataRow r2 in dt2.Rows)
              Console.WriteLine($"{r2["ID"]} - {r2["Wert2"]}");
    
            Console.WriteLine("--- Tabelle 3");
            foreach (DataRow r3 in dt3.Rows)
              Console.WriteLine($"{r3["ID"]} - {r3["Wert3"]}");
    
            Console.WriteLine("--- Ergebnis");
            foreach (var item in res)
              Console.WriteLine($"{item.ID} - {item.Wert1} - {item.Wert2} - {item.Wert3} - {item.Wert4}");
          }
    
          public class C1234
          {
            public int ID { get; set; }
            public string Wert1 { get; set; }
            public string Wert2 { get; set; }
            public string Wert3 { get; set; }
            public string Wert4 { get; set; }
          }
        }
    


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP für Developer Technologies)
    Meine Homepage mit Tipps und Tricks



    Mittwoch, 21. November 2018 08:27
  • Hallo Wolfgang,

    in Addition zu Peters Antwort könntest Du die Tables auch mit DtataTable.Merge verbinden.
    Ich habe diverse Lieferlisten abzuarbeiten, lese diese jeweils einzeiln ein, muss hier und dort etwas rein- und rauswerfen.

    Dann wird "gemerged" und bei Fehlern wird es vorher entsprechend der Quelle geworfen. Quell-Tables werden gelöscht.
    Danach kann kann man zügig weiterarbeiten.

    Falls es nicht LINQ sein muss, obwohl Deine Frage ja LINQ war

    Gruß

    Raimo


    • Bearbeitet RaimoBecker Mittwoch, 21. November 2018 10:36 TYPO
    Mittwoch, 21. November 2018 10:35