【问题标题】:Linq - Using a left join on to a table which may not have recordsLinq - 对可能没有记录的表使用左连接
【发布时间】:2017-05-09 21:22:30
【问题描述】:

假设我有 3 个表格 - 1 个标题和 2 个详细信息:

标题表

id | label
 1 | foo
 2 | bar

详细信息 1 表

id | date       | value
 1 | 2015-01-01 |     5

详情2表

id | date       | value
 1 | 2015-01-01 |     7
 2 | 2016-02-02 |    10

我想做一个连接所有三个的 linq 查询,但由于一个明细表没有另一张的记录,因此不会消除数据。结果应如下所示:

结果表

id | label | date       | value1 | value2
 1 | foo   | 2015-01-01 |      5 |      7
 2 | bar   | 2016-02-02 | <null> |     10

因此,value1 为 null,而不是删除整行。

如果我在写 SQL,我可以写

select
    h.id,
    h.label,
    coalesce(d1.date, d2.date) as date,
    d1.value as value1,
    d2.value as value2
from
    header h
    left join detail1 d1
        on d1.id = h.id
    left join detail2 d2
        on d2.id = h.id
        and (
            d2.date = d1.date
            or d1.date is null
        )

是否可以使用 Linq 编写此代码?我正在使用“on new equals new”语法,当没有匹配的 detail1 记录时,我无法弄清楚如何保留 detail2 记录。

编辑:我觉得链接的答案只回答了我问题的左连接部分。我知道我可以在 linq 中加入,但是 detail2 表同时加入了 header(不是问题)和 detail1。如果 detail1 在 detail2 中没有日期的记录,则 detail2 记录将不会出现在结果中。使用“select new{} equals new{}”不允许我在equals之前使用detail2对象,所以我不能写

from
    h in header.AsEnumerable()
join d1.AsEnumerable().DefaultIfEmpty()
    on p.Id equals d1.Id
join d2.AsEnumerable().DefaultIfEmpty()
    on new {
        Id = h["Id"],
        Date = d1["Date"] ?? d2["Date"], // Doesn't work, can't use d2 here.
    }                                    // d1 may not have a record, so there may not be a match
    equals new {
        Id = d2["Id"],
        Date = d2["Date"],
    }
select new {
    // etc...
}

【问题讨论】:

  • LEFT OUTER JOIN in LINQ的可能重复
  • LINQ 支持左连接。请参阅 lambda 语法中使用的 .GroupJoin(inner, key, key, result) 扩展方法,或经典 linq 中使用的 .DefaultIfEmpty()。您可以在this answer in question "LEFT OUTER JOIN in LINQ" 中看到后者——它是from p in ps.DefaultIfEmpty()ps 连接将被转换为 LEFT JOIN。
  • 这不是一个简单的左外连接,因为 LINQ 中的连接是等值连接,这在第二个 LEFT JOIN 上有一个额外的条件。

标签: c# linq


【解决方案1】:

要实现任意条件的连接,您需要使用另一个带有wherefrom 子句来处理您的条件。我不确定如果与 Linq to SQL 一起使用会产生什么类型的 SQL,你可能会更好地使用我的 FullOuterJoin/LeftOuterJoin IQueryable 扩展。

var ans = from h in header
          join d1 in detail1 on h.id equals d1.id into hd1j
          from hd1 in hd1j.DefaultIfEmpty()
          from d2 in detail2 where h.id == d2.id && (hd1?.date == null || hd1.date == d2?.date)
          select new { h.id, h.label, date = hd1?.date ?? d2?.date, value1 = hd1?.value, value2 = d2?.value };

对于我的Enumerable 测试,我放入了条件运算符。如果针对 IQueryable(例如 Linq to SQL)进行测试,您应该删除它们。

【讨论】:

  • 太棒了!我还没有在我的实际代码中测试它,但这看起来像我需要的。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-18
  • 2018-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多