LINQ LEFT JOIN 不适用于 NULL 值

Posted

技术标签:

【中文标题】LINQ LEFT JOIN 不适用于 NULL 值【英文标题】:LINQ LEFT JOIN not working on NULL values 【发布时间】:2015-03-07 16:27:35 【问题描述】:

我有两张表StudentMarks

Student 表有以下字段: StudentID,Name,MarkID(Nullable)Marks 表有以下字段: MarkID,Mark

学生桌

StudentID   Name    MarkID

1           Mark    1 

2           Mike    NULL

3           John    NULL

4           Paul    2

标记表格

MarkID  Mark

1       80

2       100

如果我使用左连接,那么我只会得到 markpaul 记录。 我想要左表中的所有记录(Student) 我的查询是:

   var query = (from s in Students  
               join m in Marks on s.MarkID equals m.MarkID 
               into mar from subMark in mar.DefaultIfEmpty()
               where(m.Mark > 80)
               Select s.Name)
               .ToList() 

注意:这只是一个示例。 使用左连接连接两个表并在第二个表上应用 where 条件时,如果第一个表中连接的列值为 null,则不会从第一个表中带入记录。

【问题讨论】:

我很困惑,如果您只是选择学生姓名,为什么要首先加入? @mattytommo:我想这只是一个测试。 @mattytommo:这只是一个例子。 【参考方案1】:

NULL 比较总是错误。这就是 SQL 的三值逻辑的工作方式。如果你想匹配值都为空的行,你应该使用一个检查它们是否为空的语句。

在 SQL 语句中你会写:

ON S.MARKID=M.MARKID OR (S.MARKID IS NULL AND M.MARKID IS NULL)

在 C# 中,您可以使用比较运算符,您的 LINQ 提供程序会将其转换为 IS NULL,例如:

on s.MarkID == m.MarkID || (s.MarkID == null && m.MarkID==null)

【讨论】:

先生如何在 C# 中的 LINQ 中检查它 在 C# 中,只需将值与 null 进行比较,例如 S.MARKID==null。 EF 或 LINQ-to-SQL 会将其转换为 IS NULL 这确实有效。它被翻译为我们大多数人习惯于使用 SQL 中的可选参数。唯一的事情是:例如,如果 MarkID 是一个 int,Intellisense 会抱怨并生成警告,因为表达式总是错误的,尽管它会生成正确的 SQL。不知道如何“解决”这个问题。 @jpgrassi 根本不使用 int。否则,确实为该 int 属性返回 NULL 的任何查询都将引发异常。 C# 中的 int 相当于 SQL 中的 INT NOT NULL。另请注意,外连接可能会为丢失的记录返回空值 我刚刚在这里进行了测试。我有两张桌子。餐厅和民意调查。在投票表中,我有一个 int 列,其中包含该投票的周数。如果我想选择所有餐厅并为每个餐厅带来多少票,我需要与 Poll 表进行左连接。但是,我需要过滤以仅带来本周的选票,当然还要考虑没有任何选票的餐厅。我所做的是:(x.WeekNumber == weekNum || x.WeekNumber == null)。令我惊讶的是,它奏效了。【参考方案2】:

问题是我们在左连接中使用了where子句,所以它会丢弃空值记录。

var sampleQuery= (from f in food 
            join j in juice on f.ID equals j.ID into juiceDetails from juice in juiceDetails.DefaultIfEmpty()
            where(!j.deleted)
            join fr in fruit on f.ID equals fr.ID into fruitDetails from fruit in fruitDetails.DefaultIfEmpty()
            where(!fr.deleted)
            select new
            
            // codes

            );

我们必须检查表本身的 where 子句,而不是这样。像这样

var sampleQuery= (from f in food 
            join j in juice.Table().where(x=>!x.deleted) on f.ID equals j.ID into juiceDetails from juice in juiceDetails.DefaultIfEmpty()              
            join fr in fruit.Table().where(x=>!x.deleted) on f.ID equals fr.ID into fruitDetails from fruit in fruitDetails.DefaultIfEmpty()                
            select new
            
            // codes

            );

它会正常工作的。 谢谢。

【讨论】:

【参考方案3】:

/编辑:我的第一个答案是使用完全外连接。这太过分了,可能是错误的或不完全正确。

新答案使用 LEFT OUTER JOIN。我使用 LinqPad 创建了一些示例数据来获得一个工作示例。如果您不使用 LinqPad,请忽略 .Dump() 方法。

var Students = new List<Student>() 
        new Student() StudentId = 1, Name ="John", MarkId = 1,
        new Student() StudentId = 1, Name ="Paul", MarkId = 1,
        new Student() StudentId = 1, Name ="Steve", MarkId = 1,
        new Student() StudentId = 1, Name ="John", MarkId = 2,
        new Student() StudentId = 1, Name ="Paul", MarkId = 3,
        new Student() StudentId = 1, Name ="Steve", MarkId = 1,
        new Student() StudentId = 1, Name ="Paul", MarkId = 3,
        new Student() StudentId = 1, Name ="John"  ,
        new Student() StudentId = 1, Name ="Steve"  ,
        new Student() StudentId = 1, Name ="John", MarkId = 1
        
    ;
    
    var Marks = new List<Mark>() 
        new Mark() MarkId = 1, Value = 60,
        new Mark() MarkId = 2, Value = 80,
        new Mark() MarkId = 3, Value = 100
    ;
    
    var StudentMarks = Students
                        .GroupJoin(
                            Marks,
                            st => st.MarkId,
                            mk => mk.MarkId,
                            (x,y) => new 
                                            StudentId = x.StudentId, 
                                            Name = x.Name,
                                            Mark = y.Select (z => z.Value).SingleOrDefault()
                                          
                        )
                        .Dump();

    


public class Student

    public int StudentId  get; set; 
    public string Name  get; set; 
    public int MarkId  get; set; 


public class Mark

    public int MarkId  get; set; 
    public int Value  get; set; 

输出:

您可以在我的学生列表中看到,有 2 名学生没有 MarkId。由于.SingleOrDefault(),这两个获得了分配的默认值。我认为这将解决您的问题,并为您进一步摆弄提供良好的基础。

参考: How do you perform a left outer join using linq extension methods

【讨论】:

谢谢。它会正常工作。考虑到该表都有已删除标志。我想检查已删除标志条件(在“选择新”之前,使用 Where 子句)。它没有带来完整的记录。它跳过包含 NULL 值的记录。如何解决这个问题??(对不起我的英语不好) 等一下,我将编辑我的答案,通过“简单”的 GroupJoin 为您提供解决方案。我对我的回答也不满意。 我想检查 Where 子句中的删除标志。 您既不是学生表,也不是您的标记表有删除标志。你在说什么? 我想检查两个表的已删除标志条件...考虑一个情况,两个表都有已删除标志..如何在选择值之前应用 where 子句。如果我把 where条件意味着没有完整的记录。希望你能理解。【参考方案4】:

在您的查询中,您在加入时在 Join 语句中写入了 From。 相反,您应该使用in::

   from s in Students  
    join m in Marks on s.MarkID equals m.ID into mar
    from subMark in mar.DefaultIfEmpty()
    Select s.Name).ToList() 

【讨论】:

我认为这可能是他的粘贴错误。不过是个好地方。 @Rahul:是的,我忘记了。这只是一个例子。我怀疑在加入两个表时,如果左表在 forgein 键字段中包含空值。当时空值包含记录没来。【参考方案5】:

我遇到了同样的问题。仅当 subMark 中至少有一行时,此解决方案才有效。行的 ID 无关紧要。

var query = (from s in Students  
               join m in Marks on s.MarkID equals m.MarkID into fullM
               into mar from subMark in mar.DefaultIfEmpty()
               where(m.Mark > 80)
               Select s.Name)
               .ToList() 

关键字 into 具有魔力。添加它会显示所有行,包括那些在 mar 中具有 NULL 值的行。

【讨论】:

以上是关于LINQ LEFT JOIN 不适用于 NULL 值的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 拆分字符串函数 SPLIT_STR 不适用于 LEFT JOIN。有啥问题吗?

linq用lambda表达式 left join 自连接怎么写

c# Linq left join 多个条件连接查询

Linq中使用Left Join

linq left join ,inner join ,crossjoin

Linq 多连接及 left join 实例 记录