Tek bir birleşimde birden çok alanda LINQ'da birleştirme nasıl yapılır


244

Birden fazla alanda birleştirme yapan bir LINQ2DataSet sorgusu (olarak

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

Henüz uygun bir çözüm buldum (burada bir yan tümceye fazladan kısıtlamalar ekleyebilirim, ancak bu uygun bir çözümden çok uzak veya bu çözümü kullanıyorum, ancak bu bir equijoin olduğunu varsayar).

LINQ'da tek bir birleştirmede birden çok alana katılmak mümkün müdür?

DÜZENLE

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

"Çözelti" terimi, yukandaki bir equijoin olduğunu varsayarak belirttiğim çözümdür.

Diğer DÜZENLEME

Orijinal örneğimin bir equijoin olduğu eleştirisini cevaplamak için, Şimdiki gereksinimin bir equijoin için olduğunu ve yukarıda bahsettiğim çözümü zaten kullandığımı kabul ediyorum.

Ancak, LINQ ile hangi olasılıkları ve en iyi uygulamaları kullanacağımı / kullanmam gerektiğini anlamaya çalışıyorum. Yakında bir tablo kimliğiyle bir Tarih aralığı sorgusu katılmak gerekecek ve sadece bu sorunu önleme, Görünüşe göre nerede yan tümcesinde tarih aralığı eklemek gerekir.

Her zaman olduğu gibi verilen tüm öneri ve yorumlarınız için teşekkürler


48
Bunu okuyan herkes için sadece bir FYI, eğer annon sınıflarında çoklu alan birleşimi yaparsanız, her iki annon sınıfındaki alanları da aynı şekilde adlandırmalısınız, aksi takdirde derleme hataları alırsınız.
Mark

6
Daha doğrusu, eşleşen adlara sahip olduklarından emin olmalısınız. yani anon türlerinden birinin alanlarını, diğeriyle eşleşecek şekilde adlandırabilirsiniz.
Tom Ferguson

1
Bu yanıta dikkat edin stackoverflow.com/a/34176502/1704458
TS

Ben nesneler yerine eşit her iki tarafında tuples kullandım ve o da işe yaramış gibi görünüyordu.
GHZ

Yanıtlar:


89

Anonim tipte çözüm iyi çalışmalıdır. LINQ olabilir (zaten maddeleri katılması ile birlikte) sadece equijoins temsil ve ettik neyi en orijinal sorguya dayalı zaten ifade etmek istiyorum dedi gerçekten o.

Belirli bir nedenle anonim türdeki sürümü beğenmediyseniz, bu nedeni açıklamanız gerekir.

Başlangıçta istediğinden başka bir şey yapmak istiyorsan, lütfen gerçekten ne yapmak istediğine bir örnek ver .

DÜZENLEME: Sorudaki düzenlemeye yanıt: evet, "tarih aralığı" birleştirmesi yapmak için bunun yerine where cümlesi kullanmanız gerekir. Anlamsal olarak gerçekten eşdeğerdirler, bu yüzden sadece mevcut optimizasyonlarla ilgilidir. Equijoins, iç diziye dayalı bir arama oluşturarak basit optimizasyon sağlar (LINQ'dan Nesnelere, LINQ'dan Veri Kümelerine) - bunu anahtardan o anahtarla eşleşen bir dizi girişe hashtable olarak düşünün.

Tarih aralıkları ile bunu yapmak biraz daha zordur. Bununla birlikte, bir "tarih aralığı birleştirmesi" ile tam olarak ne demek istediğinize bağlı olarak benzer bir şey yapabilirsiniz - tarihte "bantlar" oluşturmayı planlıyorsanız (örneğin yılda bir tane), aynı yıl (ancak aynı tarihte değil) eşleşmelidir, o zaman bunu sadece o bandı anahtar olarak kullanarak yapabilirsiniz. Daha karmaşıksa, örneğin birleştirmenin bir tarafı bir aralık sağlar ve birleştirmenin diğer tarafı, bu aralık içine düştüğünde eşleşirse, bir wherecümle (bir saniye sonra) daha iyi işlenirfromfıkra) IMO. Bir tarafı veya diğerini daha verimli eşleşme bulmak için sipariş ederek özellikle korkak bir sihir yapabilirsiniz, ancak bu çok fazla iş olurdu - Performansın bir sorun olup olmadığını kontrol ettikten sonra böyle bir şey yapardım.


Teşekkürler, evet performans nerede cümlecik kullanarak benim ana endişe. Ben birleştirme sonra ikinci birleştirme parametresi tanıtılarak azaltılmış olabilir daha büyük bir veri kümesi üzerinde bir filtre gerçekleştirmek nerede bir yan tümce tahmin ediyorum. Verimlilik kazanımları elde edip edemediğimi test etmek için sipariş verme fikrini beğendim
johnc

Kaç tane kaydınız olacak? Sonuçları başlamak için sipariş vermenin başlaması için belirli bir süre geçeceğini unutmayın ...
Jon Skeet

"Gerçekten anlamsal olarak eşdeğerler" - orada 'gerçekten' kelimesine ihtiyacımız var mı? Belki de " Gerçekten anlamsal olarak eşdeğerler"
demek istediniz

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 Numuneleri bu yoktu ya da gördüğüm en az olarak görmek için aradığım şey budur.
Chris Marisic

1
@PeterX gerçekten yapabilir, cevabımı burada görebilirsiniz: stackoverflow.com/a/22176658/595157
niieani

13
Yukarıdaki kod çalışmadı. on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 }
Ravi Ram

@Ravi Ram .. Teşekkürler .. yorumunuz yardımcı oldu
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 }

Sütun adları iki varlıkta farklıysa bunu yapmanız gerekir.


6
Farklı sütun adlarından bahsettiğiniz için teşekkür ederiz. Bu benim kötü ifademi düzeltti.
Gaʀʀʏ

1
Bu benim için de işe yaradı. Sütun adları eşleşmezse, "Birleştirme yan tümcesindeki ifadelerden birinin türü yanlış. 'GroupJoin' çağrısında tür çıkarımı başarısız oldu."
humbadlar

Temel değişkenleri taklit ettiğiniz için teşekkür ederiz.
Thomas.Benz

'New {}' int'in tüm özelliklerini adlandırmadığımda @humbads'ın bahsettiği hatayı aldım. Yani sadece fyi birini adlandırırsanız geri kalanını da adlandırmalısınız.
Ethan Melamed

ÇOK TEŞEKKÜR EDERİZ
Charly H

51

Bunu eşdeğer bir yöntem zinciri sözdizimi ile tamamlamak için:

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

Son argüman (x, y) => xseçtiğiniz şeyken (yukarıdaki durumda seçiyoruz x).


31

Bence daha okunabilir ve esnek bir seçenek Where fonksiyonunu kullanmaktır:

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

Bu ayrıca .DefaultIfEmpty () öğesini ekleyerek iç birleşimden sol birleşime kolayca geçiş yapılmasına izin verir.


Uzun zamandır lambda kullanıcısı olarak (soruyu sorduğumun aksine), aynı fikirdeyim
johnc

Bu daha yavaş olur mu?
AlfredBr

1
Yeni { ... } equals new { ... }sözdizimiyle aynı performansa sahip olması gerektiğini düşünüyorum . LinqPad , ifadelerin nasıl davrandığını görmek için harika bir araçtır (LINQ2SQL kullanılıyorsa SQL komut dosyası, ifade ağaçları vb.)
Alexei

Bildiğim kadarıyla INNER JOIN yerine CROSS JOIN üretiyor
Mariusz

@Mariusz Evet, INNER JOIN yerine CROSS JOIN + WHERE üretmek mantıklı. Basit sorgular için, analizörün çok benzer bir şekilde üretmesini bekliyorum.
Alexei

10
var result = from x in entity
             join y in entity2
             on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 }
             select new 
             {
               /// Columns
              };

8

gibi bir şey yapabilirsin (aşağıda)

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

Bahsettiğim gibi, bu bir equijoin gerektirir
johnc

7

Birleştirme operatörünü kullanarak yalnızca equijoins yapabilirsiniz. Diğer birleştiriciler, başka operatörler kullanılarak oluşturulabilir. Yapmaya çalıştığınız tam birleştirmenin bu yöntemleri kullanarak veya where yan tümcesini değiştirerek daha kolay olup olmayacağından emin değilim. Birleştirme maddesiyle ilgili belgeleri burada bulabilirsiniz . MSDN'nin, birleştirme işlemleri hakkında , diğer birleştirme örneklerine birden çok bağlantı içeren bir makalesi de vardır.


3

Alan adı varlıklarda farklıysa

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

Teşekkür ederim. İsim eşleme eksik olduğum parçaydı.
Brett

2

Aşağıdaki gibi görünen bir tam yöntem zinciri olarak:

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)

bu benim için çalışıyor


Bu çoklu katılım içindir, tek bir
katılımda

-3

Katılmak istediğiniz öğeleri tutmak için bir Sınıf (Tür) bildirin. Aşağıdaki örnekte JoinElement bildirimi

 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
Bu 9 yıl önce cevaplanmıştı ... bu cevap ne gibi bir değer getiriyor?
Maciej Jureczko
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.