LINQ - SQL Sol Dış Birleştirme


140

Bu sorgu bir LEFT OUTERbirleştirmeye eşdeğer mi?

//assuming that I have a parameter named 'invoiceId' of type int
from c in SupportCases
let invoice = c.Invoices.FirstOrDefault(i=> i.Id == invoiceId)
where (invoiceId == 0 || invoice != null)    
select new 
{
      Id = c.Id
      , InvoiceId = invoice == null ? 0 : invoice.Id
}

Yanıtlar:


167

Tam olarak değil - sol-dış birleşimdeki her "sol" satır 0-n "sağ" satırlarla (ikinci tabloda) eşleşeceğinden, sizinki yalnızca 0-1 ile eşleşir. Katılmak bir sol dış yapmak için, ihtiyaç SelectManyve DefaultIfEmptyörneğin:

var query = from c in db.Customers
            join o in db.Orders
               on c.CustomerID equals o.CustomerID into sr
            from x in sr.DefaultIfEmpty()
            select new {
               CustomerID = c.CustomerID, ContactName = c.ContactName,
               OrderID = x == null ? -1 : x.OrderID };   

( veya uzantı yöntemleri aracılığıyla )


19
Birisi bu çılgın sözdiziminin nasıl çalıştığını açıklayabilir mi? Bu anahtar kelimelerden herhangi birinin sihirli bir şekilde onu nasıl sol birleştirme yaptığını göremiyorum. "İnto sr" ne yapar? Linq bazen beni sinirlendiriyor :)
Joe Phillips

2
@JoePhillips Bol miktarda SQL deneyimim var ama LINQ öğrenmeye çalışmak çamurdan geçip gitmek gibi Kesinlikle deli olduğuna katılıyorum.
Nick.McDermaid

@ marc-gravell: sql sorgumu linq dönüşümünde çözmeme yardım eder misin: stackoverflow.com/questions/28367941/…
Vishal I Patil

@VishalIPatil Neden SQL'den LINQ'ye dönüştürmek istiyorsunuz? SQL gayet iyi çalışıyor ve çok daha öngörülebilir ve verimli ...
Marc Gravell

1
@VishalIPatil peki ... neden bunu yapıyorsun? Hemen hemen her LINQ aracı, elle yazılmış SQL çalıştırma yeteneğini içerir. Neden sadece bunu yapmıyorsun?
Marc Gravell

216

Şu ifadelere ihtiyacınız yoktur:

var query = 
    from customer in dc.Customers
    from order in dc.Orders
         .Where(o => customer.CustomerId == o.CustomerId)
         .DefaultIfEmpty()
    select new { Customer = customer, Order = order } 
    //Order will be null if the left join is null

Ve evet, yukarıdaki sorgu gerçekten bir LEFT OUTER birleşimi oluşturur.

Birden fazla sol birleşmeyi işleyen benzer bir soruya bağlantı: Linq - Sql: Birden çok sol dış birleşim


14
@Marc Gravvel'in cevabının işe yaradığını bilirken, bu yöntemi gerçekten tercih ediyorum çünkü IMO, sol bir birleştirmenin nasıl olması gerektiği ile daha uyumlu hissediyor.
llaughlin

1
Mükemmel cevap. 5 saatten fazla google araması mı arıyorsunuz? Bu, sonuçta SQL'in ayrılmaya bıraktığı tek yoldur.
Faisal Mq

1
ÇOK TEŞEKKÜR EDERİZ .... Tüm öğleden sonra için bir çözüm arıyordum ve kodunuz çivilenmiş (ve önyükleme yapmak doğal). Bunu birkaç kez yükseltebilseydim.
Jim

2
@Jim teşekkürler :-) Devs hala bu cevaptan kilometre alıyor sevindim. DefaultIfEmpty () 'in into ifadelerini kullanmaktan çok daha doğal hissettiğini tamamen kabul ediyorum.
Amir

7
Sadece benim yaptığım gibi bulan herkes için bir not, bu bir ÇAPRAZ UYGULAMA içinde bir SOL DIŞ birleşim ile sonuçlanır, bu da birleşmenin sağ tarafında birden fazla maç varsa kopyalar alacağınız anlamına gelir. Marc Gravell'in çözümü, "güzel" olmasa da bana aradığım uygun SQL çıktısını ve sonuç kümesini verdi.
Mike U


5

1 çözüm buldum. Linq Entity içine bu tür SQL (sol katılmak) çevirmek istiyorsanız ...

SQL:

SELECT * FROM [JOBBOOKING] AS [t0]
LEFT OUTER JOIN [REFTABLE] AS [t1] ON ([t0].[trxtype] = [t1].[code])
                                  AND ([t1]. [reftype] = "TRX")

LINQ:

from job in JOBBOOKINGs
join r in (from r1 in REFTABLEs where r1.Reftype=="TRX" select r1) 
          on job.Trxtype equals r.Code into join1
from j in join1.DefaultIfEmpty()
select new
{
   //cols...
}

Bu yoruma bakın , Linq-to-SQL varlıkları desteklemiyor DefaultIfEmpty.
TJ Crowder

2

Bir şey daha eklemek istiyorum. DB'niz düzgün bir şekilde oluşturulmuşsa ve tablolarınız yabancı anahtar kısıtlamaları ile ilişkiliyse, LINQ to SQL'de, birleştirme yapmanız gerekmez.

LINQPad kullanarak aşağıdaki LINQ sorgusunu oluşturdum:

//Querying from both the CustomerInfo table and OrderInfo table
from cust in CustomerInfo
where cust.CustomerID == 123456
select new {cust, cust.OrderInfo}

Aşağıdaki (biraz kesilmiş) sorguya çevrildi

 -- Region Parameters
 DECLARE @p0 Int = 123456
-- EndRegion
SELECT [t0].[CustomerID], [t0].[AlternateCustomerID],  [t1].[OrderID], [t1].[OnlineOrderID], (
    SELECT COUNT(*)
    FROM [OrderInfo] AS [t2]
    WHERE [t2].[CustomerID] = [t0].[CustomerID]
    ) AS [value]
FROM [CustomerInfo] AS [t0]
LEFT OUTER JOIN [OrderInfo] AS [t1] ON [t1].[CustomerID] = [t0].[CustomerID]
WHERE [t0].[CustomerID] = @p0
ORDER BY [t0].[CustomerID], [t1].[OrderID]

LEFT OUTER JOINYukarıdakilere dikkat edin .


1

Performansa dikkat edin:

En azından EF Core ile burada verilen farklı cevapların farklı performansa neden olabileceğini deneyimledim . OP'nin Linq to SQL hakkında sorduğunun farkındayım, ama bana öyle geliyor ki EF Core ile de aynı sorular ortaya çıkıyor.

Ele almak zorunda olduğum özel bir durumda, Marc Gravell'in (sözdizimsel olarak daha güzel) önerisi, bir haç içinde sol birleşmelerle sonuçlandı - Mike U'nun tarif ettiği gibi - bu özel sorgu için tahmini maliyetlerin iki olduğu sonucuna vardı. çapraz birleşim olmayan bir sorguya göre daha yüksek . Sunucu yürütme süreleri 3 kat farklıydı . [1]

Marc Gravell'in çözümü çapraz birleşmeler olmadan bir sorgu ile sonuçlandı.

Bağlam: Aslında her biri tekrar başka bir tablo için birleştirme gerektiren iki tabloda iki sol birleştirme yapmak gerekiyordu. Ayrıca, sol birleşimi uygulamak için gereken tablolarda başka nerede koşullar belirtmek zorunda kaldım. Ayrıca, ana masada iki iç birleşim vardı.

Tahmini operatör maliyetleri:

  • çapraz uygulama ile: 0.2534
  • çapraz uygulama olmadan: 0.0991.

Ms cinsinden sunucu yürütme süreleri (sorgular 10 kez yürütülür; SET STATISTICS TIME ON kullanılarak ölçülür):

  • çapraz uygulama ile: 5, 6, 6, 6, 6, 6, 6, 6, 6, 6
  • çapraz uygulama olmadan: 2, 2, 2, 2, 2, 2, 2, 2, 2, 2

(İlk çalıştırma her iki sorgu için daha yavaştı; bir şey önbelleğe alınmış gibi görünüyor.)

Tablo boyutları:

  • ana tablo: 87 sıra,
  • sol birleşim için ilk tablo: 179 sıra;
  • sol birleşim için ikinci tablo: 7 satır.

EF Çekirdek sürümü: 2.2.1.

SQL Server sürümü: MS SQL Server 2017 - 14 ... (Windows 10'da).

İlgili tüm tabloların yalnızca birincil anahtarlarda dizinleri vardı.

Sonuç: gerçekten farklı olabileceğinden, daima üretilen SQL'e bakmanız önerilir.


[1] İlginç bir şekilde, MS SQL Server Management Studio'da 'İstemci istatistikleri' ayarlanırken karşıt bir eğilim görebiliyordum; yani çapraz uygulama olmadan çözeltinin son çalışması 1 saniyeden fazla sürdü. Sanırım burada bir şeyler ters gidiyordu - belki benim kurulumumda.

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.