复杂的 Linq to SQL 查询
Complicated Linq to SQL Query
我正在努力弄清楚 Linq-to-SQL 语法对于特定查询是什么。我可以在 SQL 中轻松完成此操作,但我无法在 Linq 中完全获得正确的语法。
我在两个数据库表中有 parent 和 child 记录,由外键链接。我希望我的结果基于这些规则 return 行:
- Return 每个 parent 恰好 1 行 ,无论存在多少 children。
- Return null/zero 值,如果 child 不存在。
- Return 来自具有空条件的 child 的相关数据。如果存在多个空条件,则 return 只是 第一个 一个。
- Return 具有 non-null 条件的 child 条记录的计数。
我在 .NET Fiddle 中研究这个问题有一段时间了,但一直做不好。这是我目前所拥有的(忽略随机描述!):
IEnumerable<Parent> parents = new Parent[] {
new Parent { ID = 1, Description = "Apple" },
new Parent { ID = 2, Description = "Orange" },
new Parent { ID = 3, Description = "Pear" },
new Parent { ID = 4, Description = "Banana" } };
IEnumerable<Child> children = new Child[] {
new Child { ID = 1, ParentID = 2, Description = "Mercury", Condition = null },
new Child { ID = 2, ParentID = 3, Description = "Venus", Condition = null },
new Child { ID = 3, ParentID = 3, Description = "Earth", Condition = null },
new Child { ID = 4, ParentID = 4, Description = "Mars", Condition = null },
new Child { ID = 5, ParentID = 4, Description = "Saturn", Condition = "> 5" } };
/// What goes here...?
var query = from p in parents
join c in children on p.ID equals c.ParentID into jc
from subchildren in jc.DefaultIfEmpty()
select new Item {
ParentID = p.ID,
Description = p.Description,
PrimaryChildID = subchildren == null ? 0 : subchildren.ID,
SubDescription = subchildren == null ? null : subchildren.Description,
ConditionalCount = 0};
foreach (var item in query)
Console.WriteLine("{0} {1} {2} {3} {4}",
item.ParentID,
item.PrimaryChildID,
item.Description,
item.SubDescription,
item.ConditionalCount);
我从中得到的输出是:
1 0 Apple 0
2 1 Orange Mercury 0
3 2 Pear Venus 0
3 3 Pear Earth 0
4 4 Banana Mars 0
4 5 Banana Saturn 0
但我想要这个:
1 0 Apple 0
2 1 Orange Mercury 0
3 2 Pear Venus 0
4 4 Banana Mars 1
任何人都可以帮助我了解此查询的正确语法吗?
这应该可以做到
var query = (from p in parents
select new
{
ParentID = p.ID,
Description = p.Description,
PrimaryChildID = children.Where(c => c.ParentID == p.ID && c.Condition == null).Count() == 0 ? 0 : children.OrderBy(c=>c.ID).FirstOrDefault(c => c.ParentID == p.ID && c.Condition == null).ID,
SubDescription = children.Where(c => c.ParentID == p.ID && c.Condition == null).Count() == 0 ? null : children.OrderBy(c => c.ID).FirstOrDefault(c => c.ParentID == p.ID && c.Condition == null).Description,
ConditionalCount = children.Where(c => c.ParentID == p.ID && c.Condition != null).Count()
}).ToList();
您的情况不需要 left join
,您需要 group join
。
根据MSDN:-
The group join is useful for producing hierarchical data structures.
It pairs each element from the first collection with a set of correlated elements
from the second collection.
这样做:-
var query = from p in parents
join c in children
on p.ID equals c.ParentID into g
let firstNullElement = g.FirstOrDefault(x => x.Condition == null)
select new
{
ParentID = p.ID,
PrimaryChildID = firstNullElement != null ? firstNullElement.ID : 0,
Description = p.Description,
SubDescription = firstNullElement!= null ? firstNullElement.Description
: String.Empty,
ConditionalCount = g.Count(x => x.Condition != null)
};
为了正确解释,这里是在我们使用 select new { }
投影我们实际需要的数据之前将生成的内容(证明 Group Join
的定义):-
ParentId g
----------------------------------------------
1 null
2 ID = 1, ParentID = 2, Description = "Mercury", Condition = null
3 ID = 2, ParentID = 3, Description = "Venus", Condition = null
ID = 3, ParentID = 3, Description = "Earth", Condition = null
4 ID = 4, ParentID = 4, Description = "Mars", Condition = null
ID = 5, ParentID = 4, Description = "Saturn", Condition = "> 5"
现在,由于 g
持有 IEnumerable
个子元素,我们可以应用过滤器、项目数据、计数或做任何我们想做的事情,就像我们在最后的声明中使用 select
.而且,正如我们所见,没有任何数据来自不同的子元素。
这是完整的Working Fiddle。
这是我的查询变体:
var query = from p in parents
join c in children on p.ID equals c.ParentID into jc
from subchildren in jc.DefaultIfEmpty()
select new
{
Parent = p,
Subchildren = subchildren
} into itemData
group itemData by itemData.Parent into g
select new Item
{
ParentID = g.Key.ID,
Description = g.Key.Description,
PrimaryChildID = g.Select(_ => _.Subchildren == null ? 0 : _.Subchildren.ID).FirstOrDefault(),
SubDescription = g.Select(_ => _.Subchildren == null ? null : _.Subchildren.Description).FirstOrDefault(),
ConditionalCount = 0
};
我正在努力弄清楚 Linq-to-SQL 语法对于特定查询是什么。我可以在 SQL 中轻松完成此操作,但我无法在 Linq 中完全获得正确的语法。
我在两个数据库表中有 parent 和 child 记录,由外键链接。我希望我的结果基于这些规则 return 行:
- Return 每个 parent 恰好 1 行 ,无论存在多少 children。
- Return null/zero 值,如果 child 不存在。
- Return 来自具有空条件的 child 的相关数据。如果存在多个空条件,则 return 只是 第一个 一个。
- Return 具有 non-null 条件的 child 条记录的计数。
我在 .NET Fiddle 中研究这个问题有一段时间了,但一直做不好。这是我目前所拥有的(忽略随机描述!):
IEnumerable<Parent> parents = new Parent[] {
new Parent { ID = 1, Description = "Apple" },
new Parent { ID = 2, Description = "Orange" },
new Parent { ID = 3, Description = "Pear" },
new Parent { ID = 4, Description = "Banana" } };
IEnumerable<Child> children = new Child[] {
new Child { ID = 1, ParentID = 2, Description = "Mercury", Condition = null },
new Child { ID = 2, ParentID = 3, Description = "Venus", Condition = null },
new Child { ID = 3, ParentID = 3, Description = "Earth", Condition = null },
new Child { ID = 4, ParentID = 4, Description = "Mars", Condition = null },
new Child { ID = 5, ParentID = 4, Description = "Saturn", Condition = "> 5" } };
/// What goes here...?
var query = from p in parents
join c in children on p.ID equals c.ParentID into jc
from subchildren in jc.DefaultIfEmpty()
select new Item {
ParentID = p.ID,
Description = p.Description,
PrimaryChildID = subchildren == null ? 0 : subchildren.ID,
SubDescription = subchildren == null ? null : subchildren.Description,
ConditionalCount = 0};
foreach (var item in query)
Console.WriteLine("{0} {1} {2} {3} {4}",
item.ParentID,
item.PrimaryChildID,
item.Description,
item.SubDescription,
item.ConditionalCount);
我从中得到的输出是:
1 0 Apple 0
2 1 Orange Mercury 0
3 2 Pear Venus 0
3 3 Pear Earth 0
4 4 Banana Mars 0
4 5 Banana Saturn 0
但我想要这个:
1 0 Apple 0
2 1 Orange Mercury 0
3 2 Pear Venus 0
4 4 Banana Mars 1
任何人都可以帮助我了解此查询的正确语法吗?
这应该可以做到
var query = (from p in parents
select new
{
ParentID = p.ID,
Description = p.Description,
PrimaryChildID = children.Where(c => c.ParentID == p.ID && c.Condition == null).Count() == 0 ? 0 : children.OrderBy(c=>c.ID).FirstOrDefault(c => c.ParentID == p.ID && c.Condition == null).ID,
SubDescription = children.Where(c => c.ParentID == p.ID && c.Condition == null).Count() == 0 ? null : children.OrderBy(c => c.ID).FirstOrDefault(c => c.ParentID == p.ID && c.Condition == null).Description,
ConditionalCount = children.Where(c => c.ParentID == p.ID && c.Condition != null).Count()
}).ToList();
您的情况不需要 left join
,您需要 group join
。
根据MSDN:-
The group join is useful for producing hierarchical data structures.
It pairs each element from the first collection with a set of correlated elements
from the second collection.
这样做:-
var query = from p in parents
join c in children
on p.ID equals c.ParentID into g
let firstNullElement = g.FirstOrDefault(x => x.Condition == null)
select new
{
ParentID = p.ID,
PrimaryChildID = firstNullElement != null ? firstNullElement.ID : 0,
Description = p.Description,
SubDescription = firstNullElement!= null ? firstNullElement.Description
: String.Empty,
ConditionalCount = g.Count(x => x.Condition != null)
};
为了正确解释,这里是在我们使用 select new { }
投影我们实际需要的数据之前将生成的内容(证明 Group Join
的定义):-
ParentId g
----------------------------------------------
1 null
2 ID = 1, ParentID = 2, Description = "Mercury", Condition = null
3 ID = 2, ParentID = 3, Description = "Venus", Condition = null
ID = 3, ParentID = 3, Description = "Earth", Condition = null
4 ID = 4, ParentID = 4, Description = "Mars", Condition = null
ID = 5, ParentID = 4, Description = "Saturn", Condition = "> 5"
现在,由于 g
持有 IEnumerable
个子元素,我们可以应用过滤器、项目数据、计数或做任何我们想做的事情,就像我们在最后的声明中使用 select
.而且,正如我们所见,没有任何数据来自不同的子元素。
这是完整的Working Fiddle。
这是我的查询变体:
var query = from p in parents
join c in children on p.ID equals c.ParentID into jc
from subchildren in jc.DefaultIfEmpty()
select new
{
Parent = p,
Subchildren = subchildren
} into itemData
group itemData by itemData.Parent into g
select new Item
{
ParentID = g.Key.ID,
Description = g.Key.Description,
PrimaryChildID = g.Select(_ => _.Subchildren == null ? 0 : _.Subchildren.ID).FirstOrDefault(),
SubDescription = g.Select(_ => _.Subchildren == null ? null : _.Subchildren.Description).FirstOrDefault(),
ConditionalCount = 0
};