LINQ to SQL左外部联接


140

此查询是否等同于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
}

Answers:


167

不太完全-因为左外连接中的每个“左”行都将匹配0-n个“右”行(在第二个表中),而-您的匹配仅0-1。要进行左外部联接,您需要SelectManyDefaultIfEmpty,例如:

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

或通过扩展方法


19
有人可以解释这种疯狂的语法如何工作吗?我看不到这些关键字如何神奇地使其成为左联接。“ into sr”是做什么的?Linq有时让我感到沮丧:)
Joe Phillips

2
@JoePhillips我有很多SQL经验,但是尝试学习LINQ就像在泥泞中跋涉一样。我同意这绝对是疯狂的。
Nick.McDermaid 2014年

@马克- gravell:你能不能帮我解决我的SQL查询LINQ转换:stackoverflow.com/questions/28367941/...
维沙尔我帕蒂尔

@VishalIPatil 为什么要从SQL转换为LINQ?SQL可以正常工作,并且更具可预测性和效率……
Marc Gravell

1
@VishalIPatil所以...为什么这样做?几乎每个LINQ工具都具有运行手写SQL的功能。为什么不这样做呢?
马克·格雷夫

216

您不需要in语句:

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:多个左外部联接


14
虽然我知道@Marc Gravvel的答案确实有效,但我确实更喜欢这种方法,因为IMO感觉上更符合左联接的外观。
llaughlin

1
极好的答案。寻找超过5个小时的Google搜索。这是导致SQL保留联接的唯一方法。
Faisal Mq 2013年

1
非常感谢...。整个下午,我一直在寻找解决方案,而您的代码钉了它(启动起来很自然)。希望我能多次投票赞成。
吉姆(Jim)

2
@Jim谢谢:-)我很高兴开发人员仍然可以从这个答案中获得更多收益。我完全同意DefaultIfEmpty()比使用into语句自然得多。
阿米尔2013年

7
就像其他人一样,只是给我一个注释,这会导致在CROSS APPLY 有一个LEFT OUTER JOIN ,这意味着如果联接的右侧有多个匹配项,您将得到重复项。Marc Gravell的解决方案虽然不那么“漂亮”,但是却为我提供了我想要的正确的SQL输出和结果集。
Mike U


5

我找到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...
}

看到此注释,Linq-to-SQL实体不支持DefaultIfEmpty
TJ Crowder

2

我想再添加一件事。在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上面的内容。


1

注意性能:

我至少在EF Core上经历过,这里给出的不同答案可能会导致不同的性能。我知道OP询问了有关Linq to SQL的问题,但在我看来EF Core也出现了同样的问题。

在我必须处理的特定情况下,Marc Gravell的(在语法上更好)的建议导致了交叉应用内的左连接-与Mike U所描述的类似- 结果是,此特定查询的估计成本为2是没有交叉连接的查询的两倍。服务器执行时间相差3。[1]

Marc Gravell的解决方案导致查询没有交叉联接。

内容:本质上,我需要在两个表上执行两个左联接,每个左联接又需要联接到另一个表。此外,我还必须在需要应用左联接的表上指定其他where条件。另外,我在主表上有两个内部联接。

估计的运营商成本:

  • 交叉申请: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秒钟的时间。我想这里可能出了问题-可能是我的设置出现了问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.