【问题标题】:LEFT OUTER JOIN implemented with method syntax使用方法语法实现的 LEFT OUTER JOIN
【发布时间】:2021-12-20 11:50:41
【问题描述】:

上下文

Itzik Ben-Gan 的T-SQL Fundamentals Third Edition一书在第 3 章中包含以下查询:

SELECT C.custid, C.companyname, O.orderid
FROM Sales.Customers AS C
  LEFT OUTER JOIN Sales.Orders AS O
    ON C.custid = O.custid;

我已将其转换为 LINQ,如下所示:

var result =
    from customer in db.Customers
    join order in db.Orders
    on customer.Custid equals order.Custid into Abc
    from abc in Abc.DefaultIfEmpty()
    select new
    {
        customer.Custid,
        customer.Companyname,
        orderid = abc == null ? -1 : abc.Orderid
    };

问题

使用方法语法而不是查询语法来编写上述内容的好方法是什么?

我从这个开始:

var result = db.Customers.Join(
    db.Orders,
    customer => customer.Custid,
    order => order.Custid,
    (customer, order) =>
        new
        {
            customer.Custid,
            customer.Companyname,
            orderid = order.Orderid
        }
    );

但是,这当然会忽略 NULL 值的项目。

我不清楚的部分是如何将into语法转换为方法语法。

欢迎提出任何建议!

注意事项

如果您真的想自己运行查询,上述查询位于此处提供的项目中:

https://github.com/dharmatech/TSqlEf/blob/master/Chapter3p114/Program.cs

有关如何设置数据库,请参阅项目自述文件:

https://github.com/dharmatech/TSqlEf

【问题讨论】:

  • @CaiusJard,它不仅缺少DefaultIfEmpty,而且也没有使用GroupJoin,这似乎是必需的。
  • @CaiusJard,感谢您关于发布答案而不是编辑的建议。我已从问题中删除了答案并将其添加为单独的答案。
  • 只有在 Join 丢弃非匹配项(没有任何订单的客户)的意义上它是“必需的”,而 GroupJoin 可以给出“customer_with_list_of_orders_that_may_be_empty”,然后可以用于模拟数据库如何离开联接,因为您拥有这种“列表中具有多个细节的单个主控”结构,可以通过迭代扩展为“N 个主控,具有 N 个细节,每个细节一个重复主控”

标签: c# linq tsql entity-framework-core


【解决方案1】:

对于左连接方法语法,您需要使用 GroupJoin 以及 DefaultIfEmpty 和 SelectMany 方法。尝试使用以下查询,

var result = db.Customers.GroupJoin(
    db.Orders,
    cust => customer.Custid,
    ord => order.Custid,
    (cust, ord) => new {cust, ord})
    .SelectMany(c => c.ord.DefaultIfEmpty(), (customer, order) =>
        new
        {
            customer.Custid,
            customer.Companyname,
            orderid = order.Orderid
        }
    ); 

【讨论】:

  • 如果你有比特币地址,请告诉我,我很乐意给你寄东西买午餐。 :-D
  • 你不需要SelectMany. See the GroupJoin`重载docs.microsoft.com/en-us/dotnet/api/…
  • (但我也很想知道Aluan是什么意思)
  • 阿銮错了,GroupJoin不够。它可以只用SelectMany 和相关子查询来完成。然而,所有这些都是通用的 LINQ 方法,而在 EF 中,当使用导航属性而不是手动连接时,事情要简单得多。将所有这些与db.Customers.SelectMany(customer => customer.Orders.DefaultIfEmpty(), (customer, order) => new { ... }) 进行比较
  • @IvanStoev,啊...我明白了。这是非常好的。我已经根据您的建议发布了答案。非常感谢!
【解决方案2】:

Ivan Stoev 的建议

Ivan 在上面的评论中建议这可以使用导航属性来完成。这是基于他的建议的完整方法:

var result = db.Customers.SelectMany(
    customer => customer.Orders.DefaultIfEmpty(),
    (customer, order) => new
    {
        customer.Custid,
        customer.Companyname,
        orderid = order == null ? -1 : order.Orderid
    });

到目前为止,它似乎是最简单和最直接的。谢谢伊万!

【讨论】:

  • 作为对此的扩展,您甚至可能不需要运行联接​​。假设你正在准备一份报告,你可以只做foreach(var c in db.Customers.Include(c => c.Orders)){ foreach(var o in c.Orders) { Console.Writeline$("Customer {c.Id} has order {o.Id}"); } },也许用if 看看!c.Orders.Any() 是否客户没有订单。本质上,我是说如果你正在生成一些客户一遍又一遍地重复的矩形数据块,纯粹是这样你就可以做foreach(var x in ..someCustomerOrderJoin..)然后你可能不需要..你可以嵌套循环它
  • @CaiusJard 啊,好的。我根据您的建议添加了另一个答案。谢谢! stackoverflow.com/a/69872705/268581
【解决方案3】:

阿布的回答

这是一个基于阿布回答的版本。我必须添加这个条件:

orderid = order == null? -1 : order.Orderid

让它工作。我也改变了一些命名。但是,它似乎确实有效!

var result = db.Customers.GroupJoin(
    db.Orders,
    customer => customer.Custid,
    order => order.Custid,
    (customer, orders) => new { customer, orders })
    .SelectMany(
        customer_orders => customer_orders.orders.DefaultIfEmpty(),
        (customer_orders, order) => new
        {
            customer_orders.customer.Custid,
            customer_orders.customer.Companyname,
            orderid = order == null? -1 : order.Orderid
        });

类似问题

看到这个类似的问题:

LEFT OUTER JOIN in LINQ

它包含一个类似于 Abu 的方法的答案。

【讨论】:

    【解决方案4】:

    Caius Jard 的建议

    Caius 提到,出于构建报告的目的,可以采用嵌套的foreach 方法。以下是基于他的建议的一种方法:

    foreach (var customer in db.Customers.Include(customer => customer.Orders))
    {
        if (customer.Orders.Any())
        {
            foreach (var order in customer.Orders)
            {
                Console.WriteLine("{0} {1} {2}",
                    customer.Custid,
                    customer.Companyname,
                    order.Orderid);
            }
        }
        else
        {
            Console.WriteLine("{0} {1} {2}",
                customer.Custid,
                customer.Companyname,
                -1);
        }
    }
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-14
      • 2014-07-14
      • 1970-01-01
      • 2018-09-08
      • 2017-06-18
      • 2020-01-10
      • 1970-01-01
      • 2014-08-10
      相关资源
      最近更新 更多