LINQ to SQL Bên ngoài tham gia


140

Là truy vấn này tương đương với một LEFT OUTERtham gia?

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

Câu trả lời:


167

Không hoàn toàn - vì mỗi hàng "bên trái" trong liên kết ngoài bên trái sẽ khớp với các hàng 0-n "bên phải" (trong bảng thứ hai), trong đó, như của bạn chỉ khớp với 0-1. Để thực hiện nối ngoài bên trái, bạn cần SelectManyDefaultIfEmpty, ví dụ:

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

( hoặc thông qua các phương thức mở rộng )


19
Ai đó có thể giải thích làm thế nào cú pháp điên này hoạt động? Tôi không thấy bất kỳ từ khóa nào kỳ diệu làm cho nó tham gia trái. "Vào sr" làm gì? Linq đôi khi làm tôi thất vọng :)
Joe Phillips

2
@JoePhillips Tôi có nhiều kinh nghiệm về SQL nhưng cố gắng học LINQ giống như lội qua bùn. Tôi đồng ý nó là hoàn toàn điên rồ.
Nick.McDilyn

@ marc-Gravell: Ông có thể giúp tôi trong việc giải quyết truy vấn của tôi sql để chuyển đổi LINQ: stackoverflow.com/questions/28367941/...
Vishal tôi Patil

@VishalIPatil tại sao bạn muốn chuyển đổi từ SQL sang LINQ? SQL hoạt động tốt và dễ dự đoán và hiệu quả hơn nhiều ...
Marc Gravell

1
@VishalIPatil vậy ... tại sao lại làm vậy? Gần như mọi công cụ LINQ đều có khả năng chạy SQL viết tay. Tại sao không làm điều đó?
Marc Gravell

216

Bạn không cần phải khai báo:

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

Và đúng vậy, truy vấn trên thực sự tạo ra một tham gia TRÁI PHIẾU.

Liên kết đến một câu hỏi tương tự xử lý nhiều liên kết bên trái: Linq đến Sql: Nhiều liên kết ngoài bên trái


14
Mặc dù tôi biết rằng câu trả lời của @Marc Gravvel có hiệu quả, tôi thực sự thích phương pháp này vì IMO nó cảm thấy phù hợp hơn với cách tham gia bên trái sẽ như thế nào.
llaughlin

1
Câu trả lời tuyệt vời. Tìm kiếm hơn 5 giờ tìm kiếm google. Đây là cách duy nhất dẫn đến việc SQL sẽ rời khỏi đó.
Faisal Mq

1
CẢM ƠN BẠN rất nhiều .... Tôi đã tìm kiếm một giải pháp cho việc này suốt buổi chiều và mã của bạn đóng đinh nó (và cảm thấy tự nhiên để khởi động). Ước gì tôi có thể nâng cấp điều này nhiều lần.
Jim

2
@Jim cảm ơn :-) Tôi rất vui vì các nhà phát triển vẫn nhận được số dặm từ câu trả lời này. Tôi hoàn toàn đồng ý rằng Default IfEmpty () cảm thấy tự nhiên hơn rất nhiều so với việc sử dụng các câu lệnh.
Amir

7
Chỉ cần một lưu ý cho bất kỳ ai khác tìm thấy điều này giống như tôi vừa làm, điều này dẫn đến TRÁI PHIẾU TRÁI PHIẾU bên trong ỨNG DỤNG CROSS, có nghĩa là bạn sẽ nhận được các bản sao nếu có nhiều kết quả ở phía bên phải của phép nối. Giải pháp của Marc Gravell, mặc dù không "đẹp" đã cho tôi đầu ra SQL và tập kết quả phù hợp mà tôi đang tìm kiếm.
Mike U

13
Public Sub LinqToSqlJoin07()
Dim q = From e In db.Employees _
        Group Join o In db.Orders On e Equals o.Employee Into ords = Group _
        From o In ords.DefaultIfEmpty _
        Select New With {e.FirstName, e.LastName, .Order = o}

ObjectDumper.Write(q) End Sub

Kiểm tra http://msdn.microsoft.com/en-us/vbasic/bb737929.aspx


Thử rất hay nhưng có vẻ như OP đang sử dụng c #. Cú pháp VB là khác nhau kỳ lạ.
Levitikon

5

Tôi tìm thấy 1 giải pháp. nếu muốn dịch loại SQL này (tham gia trái) vào Thực thể Linq ...

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

Xem bình luận này , các thực thể Linq-to-SQL không hỗ trợ DefaultIfEmpty.
TJ Crowder

2

Tôi muốn thêm một điều nữa. Trong LINQ to SQL nếu DB của bạn được xây dựng chính xác và các bảng của bạn có liên quan thông qua các ràng buộc khóa ngoài, thì bạn không cần phải tham gia gì cả.

Sử dụng LINQPad Tôi đã tạo truy vấn LINQ sau:

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

Được dịch sang truy vấn (hơi bị cắt ngắn) bên dưới

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

Lưu ý những điều LEFT OUTER JOINtrên.


1

Chăm sóc hiệu suất:

Tôi đã trải nghiệm rằng ít nhất với EF Core , các câu trả lời khác nhau được đưa ra ở đây có thể dẫn đến hiệu suất khác nhau. Tôi biết rằng OP đã hỏi về Linq với SQL, nhưng dường như với tôi cũng có những câu hỏi tương tự xảy ra với EF Core.

Trong một trường hợp cụ thể mà tôi phải xử lý, đề xuất (tốt hơn về mặt cú pháp) của Marc Gravell đã dẫn đến các kết nối trái trong một ứng dụng chéo - tương tự như những gì Mike U mô tả - kết quả là chi phí ước tính cho truy vấn cụ thể này là hai cao gấp nhiều lần so với truy vấn không có tham gia chéo . Thời gian thực hiện của máy chủ khác nhau theo hệ số 3 . [1]

Giải pháp của Marc Gravell đã dẫn đến một truy vấn mà không tham gia chéo.

Bối cảnh: Về cơ bản, tôi cần phải thực hiện hai phép nối trái trên hai bảng, mỗi bảng lại yêu cầu nối với bảng khác. Hơn nữa, ở đó tôi phải chỉ định các điều kiện khác trên các bảng mà tôi cần để áp dụng phép nối trái. Ngoài ra, tôi đã có hai tham gia bên trong trên bàn chính.

Dự toán chi phí vận hành:

  • với áp dụng chéo: 0,2534
  • không áp dụng chéo: 0,0991.

Thời gian thực hiện của máy chủ tính bằng ms (các truy vấn được thực hiện 10 lần; được đo bằng cách sử dụng THỐNG KÊ THỜI GIAN BẬT):

  • áp dụng chéo: 5, 6, 6, 6, 6, 6, 6, 6, 6, 6
  • không áp dụng chéo: 2, 2, 2, 2, 2, 2, 2, 2, 2, 2

(Lần chạy đầu tiên chậm hơn đối với cả hai truy vấn; dường như có gì đó được lưu trong bộ nhớ cache.)

Kích thước bảng:

  • bảng chính: 87 hàng,
  • bảng đầu tiên cho tham gia trái: 179 hàng;
  • bảng thứ hai cho tham gia trái: 7 hàng.

Phiên bản EF Core: 2.2.1.

Phiên bản SQL Server: MS SQL Server 2017 - 14 ... (trên Windows 10).

Tất cả các bảng có liên quan chỉ có các chỉ mục trên các khóa chính.

Kết luận của tôi: luôn luôn nên xem xét SQL được tạo vì nó thực sự có thể khác nhau.


[1] Thật thú vị, khi đặt 'Thống kê máy khách' trong MS SQL Server Management Studio, tôi có thể thấy một xu hướng ngược lại; cụ thể là lần chạy cuối cùng của giải pháp mà không áp dụng chéo mất hơn 1 giây. Tôi cho rằng có gì đó không ổn ở đây - có thể với thiết lập của tôi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.