【问题标题】:LINQ to SQL Left Outer JoinLINQ to SQL 左外连接
【发布时间】:2025-12-20 23:35:16
【问题描述】:

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

【问题讨论】:

    标签: .net database linq linq-to-sql


    【解决方案1】:

    您不需要 into 语句:

    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: Multiple left outer joins

    【讨论】:

    • 虽然我知道@Marc Gravvel 的回答确实有效,但我真的更喜欢这种方法,因为 IMO 感觉更符合左连接的样子。
    • 优秀的答案。寻找超过 5 小时的谷歌搜索。这是生成的 SQL 将保留加入其中的唯一方式。
    • 非常感谢....我整个下午都在寻找解决方案,您的代码成功了(并且感觉很自然)。希望我能多次投票。
    • @Jim 谢谢 :-) 我很高兴开发人员仍然从这个答案中获得了里程。我完全同意 DefaultIfEmpty() 感觉比使用 into 语句更自然。
    • 只是给其他像我一样发现这个的人的注释,这会导致 LEFT OUTER JOIN inside CROSS APPLY,这意味着如果有连接右侧的多个匹配项。 Marc Gravell 的解决方案虽然没有那么“漂亮”,但它为我提供了我正在寻找的正确 SQL 输出和结果集。
    【解决方案2】:

    不完全 - 因为左外连接中的每个“左”行将匹配 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 };   
    

    (or via the extension methods)

    【讨论】:

    • 有人能解释一下这种疯狂的语法是如何工作的吗?我看不到这些关键字中的任何一个如何神奇地使其成为左连接。 “into sr”有什么作用? Linq 有时让我感到沮丧 :)
    • @JoePhillips 我有丰富的 SQL 经验,但尝试学习 LINQ 就像在泥泞中跋涉。我同意这绝对是疯狂的。
    • @marc-gravell: 你能帮我解决我的 sql 查询到 linq 的转换吗:*.com/questions/28367941/…
    • @VishalIPatil 所以......为什么要这样做?几乎每个 LINQ 工具都包含运行手写 SQL 的能力。为什么不这样做呢?
    • @JoePhillips,我认为into sq 在 LINQ 术语中称为分组。它是连接操作的虚拟结果,是由所有其他连接对象组成的集合,但似乎也具有连接右侧的属性。像 x.Customerx.Order 以及 x.AllPropertiesOfOrder (因为它们循环通过 srfrom x in sr.DefaultIfEmpty()DefaultIfEmpty 只是初始化一个默认的 Order 如果连接产生空值。
    【解决方案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

    【讨论】:

    • 不错的尝试,但看起来 OP 正在使用 c#。 VB 语法奇怪地不同。
    【解决方案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...
    }
    

    【讨论】:

    • this comment,Linq-to-SQL 实体不支持DefaultIfEmpty
    【解决方案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,此处给出的不同答案可能会导致不同的性能。我知道 OP 询问了 Linq to SQL,但在我看来,EF Core 也会出现同样的问题。

      在我必须处理的特定情况下,Marc Gravell 的(语法上更好的)建议导致交叉应用内的左连接 - 类似于 Mike U 描述的 - 其结果是估计成本与没有交叉连接的查询相比,此特定查询的高两倍。服务器执行时间相差 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 服务器版本: MS SQL Server 2017 - 14...(在 Windows 10 上)。

      所有相关表仅在主键上有索引。

      我的结论:总是建议查看生成的 SQL,因为它确实可能不同。


      [1] 有趣的是,在 MS SQL Server Management Studio 中设置“客户端统计信息”时,我可以看到相反的趋势;即最后一次运行没有交叉应用的解决方案花费了超过 1 秒。我想这里出了点问题 - 可能是我的设置。

      【讨论】: