Linq - 使用左连接到可能没有记录的表

时间:2017-05-09 21:22:30

标签: c# linq

假设我有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表正在加入两个标题(不是问题)和detail1。如果detail1在detail2中没有日期记录,则detail2记录将不会出现在结果中。使用“select new {}等于new {}”不允许我在等于之前使用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...
}

1 个答案:

答案 0 :(得分:2)

要实现具有任意条件的连接,您需要使用另一个from子句和where来处理您的情况。我不确定是否与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),则应删除它们。