วิธีการรวมใน LINQ บนหลาย ๆ ฟิลด์ในการเข้าร่วมครั้งเดียว


244

ฉันต้องทำแบบสอบถาม LINQ2DataSet ที่เข้าร่วมในมากกว่าหนึ่งฟิลด์ (เช่น

var result = from x in entity
join y in entity2 
       on x.field1 = y.field1 
and 
          x.field2 = y.field2

ฉันยังพบวิธีแก้ปัญหาที่เหมาะสม (ฉันสามารถเพิ่มข้อ จำกัด พิเศษให้กับส่วนคำสั่งได้ แต่นี่อยู่ไกลจากโซลูชันที่เหมาะสมหรือใช้โซลูชันนี้แต่ถือว่าเป็น equijoin)

เป็นไปได้ใน LINQ ที่จะเข้าร่วมในหลายสาขาในการเข้าร่วมเดียว?

แก้ไข

var result = from x in entity
             join y in entity2
             on new { x.field1, x.field2 } equals new { y.field1, y.field2 }

เป็นวิธีแก้ปัญหาที่ฉันอ้างถึงโดยสมมติว่า equijoin ด้านบน

แก้ไขเพิ่มเติม

เพื่อตอบวิจารณ์ว่าตัวอย่างดั้งเดิมของฉันคือ equijoin ฉันยอมรับว่าข้อกำหนดปัจจุบันของฉันคือ equijoin และฉันได้ใช้วิธีแก้ไขปัญหาที่ฉันอ้างถึงข้างต้นแล้ว

อย่างไรก็ตามฉันพยายามทำความเข้าใจความเป็นไปได้และแนวทางปฏิบัติที่ดีที่สุดที่ฉันมี / ควรใช้กับ LINQ ฉันจะต้องทำแบบสอบถามช่วงวันที่เข้าร่วมกับ ID ตารางในไม่ช้าและเป็นเพียงการจองล่วงหน้าปัญหานั้นดูเหมือนว่าฉันจะต้องเพิ่มช่วงวันที่ในส่วนคำสั่งที่

ขอบคุณเช่นเคยสำหรับข้อเสนอแนะและความคิดเห็นทั้งหมดที่ได้รับ


48
เพียงแค่ FYI สำหรับทุกคนที่อ่านสิ่งนี้หากคุณเข้าร่วมหลายเขตข้อมูลในชั้นเรียน Annon คุณต้องตั้งชื่อเขตข้อมูลในชั้นเรียน Annon ทั้งสองแบบเดียวกันมิฉะนั้นคุณจะได้รับข้อผิดพลาดในการรวบรวม
ทำเครื่องหมาย

6
หรือคุณต้องตรวจสอบให้แน่ใจว่าพวกเขามีชื่อที่ตรงกัน เช่นคุณสามารถตั้งชื่อฟิลด์ของหนึ่งในประเภทอานนท์เพื่อให้ตรงกับอีกฟิลด์หนึ่ง
Tom Ferguson

1
ให้ความสนใจกับคำตอบนี้stackoverflow.com/a/34176502/1704458
TS

ฉันใช้สิ่งอันดับสำหรับด้านใดด้านหนึ่งแทนที่จะเป็นวัตถุและมันก็ใช้ได้เหมือนกัน
GHZ

คำตอบ:


89

การแก้ปัญหาด้วยประเภทไม่ระบุชื่อควรทำงานได้ดี LINQ สามารถแสดงอีควิจินได้เท่านั้น (พร้อมกับส่วนคำสั่งต่อไป) และนั่นคือสิ่งที่คุณพูดว่าคุณต้องการแสดงต่อไปโดยยึดตามข้อความค้นหาต้นฉบับของคุณ

หากคุณไม่ชอบรุ่นที่มีประเภทไม่ระบุชื่อด้วยเหตุผลบางประการคุณควรอธิบายเหตุผลนั้น

หากคุณต้องการที่จะทำสิ่งอื่น ๆ กว่าเดิมสิ่งที่คุณขอโปรดให้ตัวอย่างของสิ่งที่คุณจริงๆต้องการที่จะทำ

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

การทำเช่นนั้นกับช่วงวันที่ค่อนข้างยาก อย่างไรก็ตามขึ้นอยู่กับสิ่งที่คุณหมายถึงโดย "ช่วงวันที่เข้าร่วม" คุณอาจทำสิ่งที่คล้ายกัน - ถ้าคุณวางแผนที่จะสร้าง "แบนด์" ของวันที่ (เช่นหนึ่งครั้งต่อปี) เช่นสองรายการที่เกิดขึ้นใน ปีเดียวกัน (แต่ไม่ใช่ในวันที่เดียวกัน) ควรตรงกันจากนั้นคุณสามารถทำได้โดยใช้วงดนตรีนั้นเป็นกุญแจ หากมีความซับซ้อนมากขึ้นเช่นด้านหนึ่งของการรวมให้ช่วงและด้านอื่น ๆ ของการเข้าร่วมให้วันที่เดียวการจับคู่ถ้ามันอยู่ในช่วงนั้นจะได้รับการจัดการที่ดีขึ้นด้วยwhereประโยค (หลังจากที่สองfromประโยค) IMO คุณสามารถทำเวทย์มนตร์ขี้ขลาดโดยเฉพาะอย่างยิ่งโดยการสั่งซื้อด้านใดด้านหนึ่งเพื่อหาการแข่งขันที่มีประสิทธิภาพมากขึ้น แต่นั่นจะทำงานมาก - ฉันจะทำสิ่งนั้นหลังจากตรวจสอบว่าประสิทธิภาพเป็นปัญหา


ขอบคุณใช่ประสิทธิภาพเป็นปัญหาหลักของฉันด้วยการใช้ข้อ ฉันคาดเดาว่าส่วนคำสั่งไหนหลังจากการเข้าร่วมจะทำการกรองข้อมูลบนชุดข้อมูลขนาดใหญ่ที่สามารถลดลงได้โดยการแนะนำพารามิเตอร์เข้าร่วมครั้งที่สอง ฉันชอบความคิดในการสั่งซื้อเพื่อทดสอบว่าฉันจะได้รับประสิทธิภาพเพิ่มขึ้นหรือไม่
johnc

คุณจะมีกี่ระเบียน อย่าลืมว่าการสั่งให้ผลลัพธ์เริ่มต้นจะใช้เวลาพอสมควรในการเริ่มต้น ...
Jon Skeet

"พวกมันมีความหมายเทียบเท่ากันจริง ๆ " - เราต้องการคำว่า 'จริงๆ' ในนั้นไหม? บางทีคุณอาจจะหมายถึง "พวกเขากำลังจริงๆเทียบเท่าความหมาย" :)
onedaywhen

136
var result = from x in entity
   join y in entity2 on new { x.field1, x.field2 } equals new { y.field1, y.field2 }

นี่คือสิ่งที่ฉันกำลังมองหาเพื่อดูเนื่องจาก 101 Linq Samples ไม่มีสิ่งนี้หรืออย่างน้อยที่สุดที่ฉันเห็น
Chris Marisic

1
@PeterX แน่นอนมันสามารถดูคำตอบของฉันที่นี่: stackoverflow.com/a/22176658/595157
niieani

13
รหัสข้างต้นไม่ทำงาน หลังจากเพิ่มon new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 } มันทำงาน
Ravi Ram

@Ravi Ram .. ขอบคุณ .. ความคิดเห็นของคุณช่วย
NMathur

80
var result = from x in entity1
             join y in entity2
             on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 }

คุณต้องทำสิ่งนี้หากชื่อคอลัมน์แตกต่างกันในสองเอนทิตี


6
ขอขอบคุณที่พูดถึงชื่อคอลัมน์อื่น นี่เป็นการแสดงออกที่ไม่ดีของฉัน
Gaʀʀʏ

1
มันก็ใช้ได้กับฉันเช่นกัน หากชื่อคอลัมน์ไม่ตรงกันคุณจะได้รับข้อผิดพลาดนี้ "ประเภทของหนึ่งในนิพจน์ในส่วนคำสั่งเข้าร่วมไม่ถูกต้องการอนุมานประเภทล้มเหลวในการเรียก 'GroupJoin'
humbads

ขอบคุณสำหรับนามแฝงของตัวแปรหลัก
Thomas.Benz

ฉันได้รับข้อผิดพลาดที่ @humbads พูดถึงเมื่อฉันไม่ได้ตั้งชื่อคุณสมบัติทั้งหมดของ int ว่า 'new {}' ดังนั้นเพียงแค่ fyi ถ้าคุณตั้งชื่อคุณต้องตั้งชื่อที่เหลือด้วย
Ethan Melamed

ขอบคุณมาก
Charly H

51

เพียงแค่ทำสิ่งนี้ให้เสร็จสิ้นด้วยไวยากรณ์ของห่วงโซ่เมธอดที่เทียบเท่า:

entity.Join(entity2, x => new {x.Field1, x.Field2},
                     y => new {y.Field1, y.Field2}, (x, y) => x);

ในขณะที่อาร์กิวเมนต์สุดท้าย(x, y) => xคือสิ่งที่คุณเลือก (ในกรณีข้างต้นเราเลือกx)


31

ฉันคิดว่าตัวเลือกที่อ่านง่ายและยืดหยุ่นมากขึ้นคือการใช้ฟังก์ชั่น Where:

var result = from x in entity1
             from y in entity2
                 .Where(y => y.field1 == x.field1 && y.field2 == x.field2)

นอกจากนี้ยังอนุญาตให้เปลี่ยนจากการเข้าร่วมภายในเป็นการเข้าร่วมซ้ายโดยการต่อท้ายเริ่มต้น IfEmpty ()


ในฐานะที่เป็นผู้ใช้แลมบ์ดาเป็นเวลานาน (เมื่อเทียบกับตอนที่ฉันถามคำถาม) ฉันจะต้องเห็นด้วย
johnc

จะช้ากว่านี้ไหม
AlfredBr

1
ฉันคิดว่ามันควรจะมีประสิทธิภาพเช่นเดียวกับ{ ... } equals new { ... }ไวยากรณ์ใหม่ LinqPadเป็นเครื่องมือที่ดีที่จะเห็นว่าการแสดงออกมีพฤติกรรม (SQL สคริปต์ถ้า Linq2Sql จะใช้ต้นไม้แสดงออก ฯลฯ )
อเล็กซี่

เท่าที่ฉันสังเกตเห็นมันผลิต CROSS JOIN แทน INNER JOIN
Mariusz

@Mariusz ใช่มันสมเหตุสมผลแล้วที่จะสร้าง CROSS JOIN + WHERE แทนที่จะเข้าร่วม INNER สำหรับการค้นหาอย่างง่ายฉันคาดว่าเครื่องวิเคราะห์จะสร้างคล้ายกัน
Alexei


8

คุณสามารถทำสิ่งที่ชอบ (ด้านล่าง)

var query = from p in context.T1

        join q in context.T2

        on

        new { p.Col1, p.Col2 }

        equals

         new { q.Col1, q.Col2 }

        select new {p...., q......};

ดังที่ฉันกล่าวถึงในคำถามที่ต้องมี equijoin
johnc

7

การใช้ตัวดำเนินการเข้าร่วมคุณสามารถดำเนินการ equijoins เท่านั้น การเชื่อมประเภทอื่น ๆ สามารถสร้างได้โดยใช้ตัวดำเนินการอื่น ฉันไม่แน่ใจว่าการเข้าร่วมที่แน่นอนที่คุณพยายามทำนั้นจะทำได้ง่ายขึ้นหรือไม่โดยใช้วิธีการเหล่านี้หรือโดยการเปลี่ยนตำแหน่งคำสั่ง เอกสารเกี่ยวกับประโยคเข้าร่วมสามารถพบได้ที่นี่ MSDN มีบทความเกี่ยวกับการเข้าร่วมการดำเนินงานที่มีลิงก์หลายรายการไปยังตัวอย่างการเข้าร่วมอื่น ๆ เช่นกัน


3

หากชื่อฟิลด์แตกต่างกันในเอนทิตี

var result = from x in entity
   join y in entity2 on 
          new {
                field1=   x.field1,
               field2 =  x.field2 
             } 
          equals
         new { 
                field1= y.field1,
                field2=  y.myfield
              }
select new {x,y});

ขอบคุณ. การจับคู่ชื่อเป็นส่วนที่ฉันขาดหายไป
Brett

2

ในฐานะที่เป็นโซ่วิธีการเต็มรูปแบบที่จะมีลักษณะเช่นนี้:

lista.SelectMany(a => listb.Where(xi => b.Id == a.Id && b.Total != a.Total),
                (a, b) => new ResultItem
                {
                    Id = a.Id,
                    ATotal = a.Total,
                    BTotal = b.Total
                }).ToList();

-2
from d in db.CourseDispatches
                             join du in db.DispatchUsers on d.id equals du.dispatch_id
                             join u in db.Users on du.user_id equals u.id
                             join fr in db.Forumreports on (d.course_id + '_' + du.user_id)  equals  (fr.course_id + '_'+ fr.uid)

มันใช้งานได้สำหรับฉัน


นี่คือการเข้าร่วมหลายครั้งเขาต้องการที่จะเข้าร่วมกับหลายสาขาในการเข้าร่วมครั้งเดียว
กฎหมาย

-3

ประกาศ Class (Type) เพื่อเก็บองค์ประกอบที่คุณต้องการเข้าร่วม ในตัวอย่างด้านล่างประกาศJoinElement

 public class **JoinElement**
{
    public int? Id { get; set; }
    public string Name { get; set; }

}

results = from course in courseQueryable.AsQueryable()
                  join agency in agencyQueryable.AsQueryable()
                   on new **JoinElement**() { Id = course.CourseAgencyId, Name = course.CourseDeveloper } 
                   equals new **JoinElement**() { Id = agency.CourseAgencyId, Name = "D" } into temp1

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