LINQ ke SQL Left Outer Join


140

Apakah kueri ini setara dengan LEFT OUTERgabungan?

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

Jawaban:


167

Tidak cukup - karena setiap baris "kiri" di gabung-kiri-luar akan cocok dengan 0-n "kanan" baris (di tabel kedua), di mana-seperti Anda cocok hanya 0-1. Untuk melakukan gabungan luar kiri, Anda perlu SelectManydan DefaultIfEmpty, misalnya:

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

( atau melalui metode ekstensi )


19
Adakah yang bisa menjelaskan bagaimana sintaksis gila ini bekerja? Saya gagal melihat bagaimana salah satu dari kata kunci tersebut secara ajaib membuatnya menjadi join kiri. Apa yang dilakukan "ke sr"? Linq kadang membuatku frustasi :)
Joe Phillips

2
@ JoPhillips Saya punya banyak pengalaman SQL tetapi mencoba untuk belajar LINQ seperti mengarungi lumpur. Saya setuju itu benar-benar gila.
Nick.McDermaid

@ marc-gravell: Bisakah Anda membantu saya dalam menyelesaikan kueri sql saya ke konversi LINQ: stackoverflow.com/questions/28367941/…
Vishal I Patil

@VishalIPatil mengapa Anda ingin mengonversi dari SQL ke LINQ? SQL berfungsi dengan baik dan jauh lebih mudah diprediksi dan efisien ...
Marc Gravell

1
@VishalIPatil jadi ... mengapa melakukan itu? Hampir setiap alat LINQ mencakup kemampuan untuk menjalankan SQL yang ditulis tangan. Kenapa tidak lakukan itu saja?
Marc Gravell

216

Anda tidak perlu pernyataan ke:

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

Dan ya, pertanyaan di atas memang membuat bergabung LEFT OUTER.

Tautan ke pertanyaan serupa yang menangani banyak gabungan kiri: Linq ke Sql: Banyak gabungan luar kiri


14
Walaupun saya tahu bahwa jawaban @Marc Gravvel berhasil, saya benar-benar lebih suka metode ini karena IMO rasanya lebih sesuai dengan seperti apa tampilan join kiri seharusnya.
llaughlin

1
Jawaban yang sangat bagus. Mencari lebih dari 5 jam pencarian google. Ini adalah satu-satunya cara yang menghasilkan SQL akan tetap bergabung di dalamnya.
Faisal Mq

1
Terima kasih banyak ... Saya sedang mencari solusi untuk ini sepanjang sore dan kode Anda berhasil (dan terasa wajar untuk boot). Seandainya saya bisa memperbaiki ini beberapa kali.
Jim

2
@ Jim terima kasih :-) Saya senang para devs masih mendapatkan jarak tempuh dari jawaban ini. Saya sepenuhnya setuju bahwa DefaultIfEmpty () terasa lebih alami daripada menggunakan pernyataan ke dalam.
Amir

7
Hanya catatan untuk orang lain yang menemukan ini seperti yang baru saja saya lakukan, ini menghasilkan LEFT OUTER JOIN dalam CROSS APPLY, yang berarti Anda akan mendapatkan duplikat jika ada beberapa pertandingan di sisi kanan gabung. Solusi Marc Gravell, walaupun tidak "cantik" memberi saya hasil SQL yang tepat dan hasil yang saya cari.
Mike U


5

Saya menemukan 1 solusi. jika ingin menerjemahkan SQL semacam ini (gabung kiri) ke Linq Entity ...

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

Lihat komentar ini , entitas Linq-to-SQL tidak mendukung DefaultIfEmpty.
TJ Crowder

2

Saya ingin menambahkan satu hal lagi. Dalam LINQ ke SQL jika DB Anda dibangun dengan benar dan tabel Anda terkait melalui batasan kunci asing, maka Anda tidak perlu melakukan join sama sekali.

Menggunakan LINQPad saya membuat permintaan LINQ berikut:

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

Yang diterjemahkan ke kueri (sedikit terpotong) di bawah ini

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

Perhatikan hal di LEFT OUTER JOINatas.


1

Jaga kinerja:

Saya mengalami bahwa setidaknya dengan EF Core jawaban berbeda yang diberikan di sini mungkin menghasilkan kinerja yang berbeda. Saya sadar bahwa OP bertanya tentang Linq ke SQL, tetapi bagi saya sepertinya pertanyaan yang sama juga terjadi pada EF Core.

Dalam kasus tertentu yang harus saya tangani, saran (secara sintaksis lebih bagus) dari Marc Gravell menghasilkan gabungan kiri di dalam tanda silang berlaku - mirip dengan yang dideskripsikan oleh Mike U - yang menghasilkan hasil estimasi biaya untuk permintaan spesifik ini adalah dua. kali lebih tinggi dibandingkan dengan kueri tanpa gabungan silang . Waktu eksekusi server berbeda dengan faktor 3 . [1]

Solusi oleh Marc Gravell menghasilkan permintaan tanpa cross joins.

Konteks: Saya pada dasarnya perlu melakukan dua gabungan kiri pada dua tabel yang masing-masing lagi membutuhkan bergabung ke meja lain. Selain itu, di sana saya harus menentukan di mana-kondisi lain pada tabel di mana saya perlu menerapkan join kiri. Selain itu, saya memiliki dua gabungan batin di meja utama.

Perkiraan biaya operator:

  • dengan cross berlaku: 0.2534
  • tanpa silang berlaku: 0,0991.

Waktu eksekusi server dalam ms (kueri dieksekusi 10 kali; diukur menggunakan SET STATISTICS TIME ON):

  • dengan cross berlaku: 5, 6, 6, 6, 6, 6, 6, 6, 6, 6
  • tanpa tanda silang berlaku: 2, 2, 2, 2, 2, 2, 2, 2, 2, 2

(Run pertama sangat lambat untuk kedua query; tampaknya ada sesuatu yang di-cache.)

Ukuran meja:

  • tabel utama: 87 baris,
  • tabel pertama untuk join kiri: 179 baris;
  • tabel kedua untuk join kiri: 7 baris.

Versi EF Core: 2.2.1.

Versi SQL Server: MS SQL Server 2017 - 14 ... (di Windows 10).

Semua tabel yang relevan memiliki indeks pada kunci utama saja.

Kesimpulan saya: selalu disarankan untuk melihat SQL yang dihasilkan karena dapat sangat berbeda.


[1] Cukup menarik, ketika mengatur 'Statistik klien' di MS SQL Server Management Studio aktif, saya bisa melihat tren yang berlawanan; yaitu bahwa menjalankan solusi terakhir tanpa cross berlaku membutuhkan lebih dari 1s. Saya kira ada sesuatu yang salah di sini - mungkin dengan pengaturan saya.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.