2009-03-31 3 views
121

Является ли этот запрос эквивалентным для объединения LEFT OUTER?LINQ to SQL Left Outer Join

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

ответ

141

Не совсем - так как каждый «левая» строка в левом внешнее_соединение будет соответствовать 0-Н «правильных» строк (во второй таблице), где-как у вас соответствует только 0-1. Для того, чтобы сделать левое внешнее соединение, вам нужно SelectMany и DefaultIfEmpty, например:

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.OrderID == null ? -1 : x.OrderID}; 

(or via the extension methods)

+4

LINQ to Entities не распознает метод DefaultIfEmpty ... –

+13

Может кто-нибудь объяснить, как работает этот сумасшедший синтаксис? Я не вижу, как любое из этих слов волшебным образом делает его левым соединением. Что делает «в sr»? Linq иногда меня разочаровывает :) –

+1

@JoePhillips У меня много опыта SQL, но попытка узнать LINQ - это как пробираться сквозь грязь. Я согласен, что это абсолютно безумно. –

12
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 

Проверить http://msdn.microsoft.com/en-us/vbasic/bb737929.aspx

+0

Хорошая попытка, но похоже, что OP использует C#. Синтаксис VB необычно отличается. – Levitikon

+3

+1 Просто потому, что это хороший пример vb.net – twoleggedhorse

4

Я нашел 1 решение. если хотите перевести этот вид SQL (слева присоединиться) в 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... 
} 
+0

См. [Этот комментарий] (http://stackoverflow.com/questions/700523/linq-to-sql-left-outer-join#comment21835463_700580), Linq-to -SQL-объекты не поддерживают 'DefaultIfEmpty'. –

177

Вам не нужен в отчетности:

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 

И да, запрос выше действительно создать левое внешнее соединение ,

Ссылка на аналогичный вопрос, который обрабатывает несколько левых объединений: Linq to Sql: Multiple left outer joins

+10

Хотя я знаю, что ответ @Marc Gravvel действительно работает, я действительно предпочитаю этот метод, потому что IMO он больше согласуется с тем, как должно выглядеть левое соединение. – llaughlin

+1

Отличный ответ. Поиск более 5 часов поиска Google. Это единственный способ, с помощью которого SQL будет иметь в нем объединение. –

+1

СПАСИБО ВАС так много .... Я искал решение для всего этого дня, и ваш код прибил его (и чувствует себя естественно, чтобы загрузиться). Хотелось бы, чтобы я мог продвигать это несколько раз. – Jim

1

Я хотел бы добавить еще одну вещь. В LINQ to SQL, если ваша БД правильно построена, а ваши таблицы связаны с ограничениями внешнего ключа, то вам вообще не нужно делать соединение.

Использование LINQPad Я создал следующий запрос LINQ:

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

который был переведен на несколько (усеченный) запрос ниже

-- 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 JOIN выше.