LINQ to SQL Left Outer Join
Этот запрос эквивалентен соединению LEFT OUTER
?
//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
}
Ответы
Ответ 1
Не совсем - поскольку каждая "левая" строка в левом-внешнем соединении будет соответствовать 0-n "правым" строкам (во второй таблице), где - так как ваш соответствует только 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};
(или с помощью методов расширения)
Ответ 2
Вам не нужны инструкции:
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
И да, запрос выше действительно создает объединение LEFT OUTER.
Ссылка на аналогичный вопрос, который обрабатывает несколько левых объединений:
Linq to Sql: несколько левых внешних соединений
Ответ 3
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
Ответ 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...
}
Ответ 5
Я хотел бы добавить еще одну вещь. В 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
выше.
Ответ 6
Позаботьтесь о производительности:
Я понял, что по крайней мере с EF Core разные ответы, приведенные здесь, могут привести к разной производительности. Я знаю, что ОП спрашивал о Linq to SQL, но мне кажется, что те же вопросы возникают и с EF Core.
В конкретном случае, с которым мне пришлось столкнуться, (синтаксически более приятное) предложение Марка Гравелла привело к левым соединениям внутри перекрестного применения - аналогично тому, что описал Майк У - , что привело к тому, что предполагаемые затраты для этого конкретного запрос был в два раза выше по сравнению с запросом без перекрестных объединений. Время выполнения сервера отличалось в 3 раза. [1]
В результате решения Марка Гравелла был получен запрос без перекрестных объединений.
Контекст: Мне по сути нужно было выполнить два левых объединения двух таблиц, каждая из которых снова требовала соединения с другой таблицей. Кроме того, там я должен был указать другие условия where в таблицах, к которым мне нужно было применить left join.
Кроме того, у меня было два внутренних соединения на главном столе.
Ориентировочные расходы оператора:
- с крестом: 0,2534
- без перекрестного обращения: 0,0991.
Время выполнения сервера в мс (запросы выполнены 10 раз; измерено с использованием SET STATISTICS TIME ON):
- с крестиком: 5, 6, 6, 6, 6, 6, 6, 6, 6, 6
- без крестика применяются: 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
(Самый первый запуск был медленнее для обоих запросов; похоже, что-то кэшировано.)
Размеры стола:
- основная таблица: 87 строк,
- первая таблица для левого соединения: 179 строк;
- вторая таблица для левого соединения: 7 строк.
Версия EF Core: 2.2.1.
Версия SQL Server:
MS SQL Server 2017 - 14... (в Windows 10).
Все соответствующие таблицы имеют индексы только для первичных ключей.
Мой вывод: всегда рекомендуется смотреть на сгенерированный SQL, поскольку он действительно может отличаться.
[1] Интересно, что при установке "Статистика клиента" в MS SQL Server Management Studio я увидел противоположную тенденцию; а именно, что последний запуск решения без перекрестного применения занял более 1 с. Я полагаю, что здесь что-то не так - возможно, с моей настройкой.