LINQ LEFT JOIN 不适用于 NULL 值
LINQ LEFT JOIN not working on NULL values
我有两个 tables Student 和 Marks.
学生 table有以下字段:
StudentID
、Name
、MarkID(Nullable)
。
标记 table 具有以下字段:
MarkID
,Mark
Student table
StudentID Name MarkID
1 Mark 1
2 Mike NULL
3 John NULL
4 Paul 2
Mark table
MarkID Mark
1 80
2 100
如果我使用左连接,那么我只会得到 mark
和 paul
记录。
我想要左边的所有记录 table(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()
注意:这只是一个例子。
使用左连接连接两个 table 并在第二个 table 上应用 where 条件时,如果连接的列值在第一个 table 中为空,则不会从第一个 table.
在您的查询中,您在加入时在 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()
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)
/编辑:我的第一个答案是使用 FULL OUTER JOIN。这太过分了,可能是错误的或不完全正确。
新答案使用 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()
,这 2 个获得了分配的默认值。我认为这将解决您的问题,并为您进一步调整打下良好的基础。
参考资料:
How do you perform a left outer join using linq extension methods
问题是我们在 Left join.So 中使用 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
});
取而代之,我们必须检查 table itself.Like 中的 where 子句 this
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
});
它将正常工作。
谢谢。
我遇到了同样的问题。只有在 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 值的行
我有两个 tables Student 和 Marks.
学生 table有以下字段:
StudentID
、Name
、MarkID(Nullable)
。
标记 table 具有以下字段:
MarkID
,Mark
Student table
StudentID Name MarkID
1 Mark 1
2 Mike NULL
3 John NULL
4 Paul 2
Mark table
MarkID Mark
1 80
2 100
如果我使用左连接,那么我只会得到 mark
和 paul
记录。
我想要左边的所有记录 table(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()
注意:这只是一个例子。 使用左连接连接两个 table 并在第二个 table 上应用 where 条件时,如果连接的列值在第一个 table 中为空,则不会从第一个 table.
在您的查询中,您在加入时在 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()
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)
/编辑:我的第一个答案是使用 FULL OUTER JOIN。这太过分了,可能是错误的或不完全正确。
新答案使用 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()
,这 2 个获得了分配的默认值。我认为这将解决您的问题,并为您进一步调整打下良好的基础。
参考资料: How do you perform a left outer join using linq extension methods
问题是我们在 Left join.So 中使用 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
});
取而代之,我们必须检查 table itself.Like 中的 where 子句 this
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
});
它将正常工作。 谢谢。
我遇到了同样的问题。只有在 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 值的行