我在将SQL查询转换为相应的EF查询时遇到问题。我很接近,但我认为我错过了左连接的东西。
这是我的SQL(一个有点人为的例子):
SELECT
Count(*), -- count posts
Tag.Name,
ISNULL(Category.Name, 'Other')
FROM Post
INNER JOIN Tag ON Post.TagID=Tag.ID
LEFT OUTER JOIN Category ON Tag.CategoryID=Category.ID
GROUP BY
Tag.Name, ISNULL(Category.Name, 'Other')
帖子有0-1个标签(就像我说的,有点做作的例子)。标签有0-1类别。因此,INNER
和LEFT
联接非常重要。
这是我不太正确的EF查询:
var counts = ctx.Posts
.GroupBy(po =>
new
{
Tag = po.Tag.Name,
Category = po.Tag.Category.Name ?? "Other"
})
.Select(agg =>
new
{
NumberOfPosts = agg.Count(),
Tag = agg.Key.Tag,
Category = agg.Key.Category
})
.ToList();
此EF查询导致此SQL查询,这不是正确的:
SELECT
1 AS [C1],
[GroupBy1].[A1] AS [C2],
[GroupBy1].[K1] AS [Name],
[GroupBy1].[K2] AS [C3]
FROM ( SELECT
[Join2].[K1] AS [K1],
[Join2].[K2] AS [K2],
COUNT([Join2].[A1]) AS [A1]
FROM ( SELECT
[Extent2].[Name] AS [K1],
CASE WHEN ([Extent3].[Name] IS NULL) THEN N'Other' ELSE [Extent3].[Name] END AS [K2],
1 AS [A1]
FROM [dbo].[Post] AS [Extent1]
LEFT OUTER JOIN [dbo].[Tag] AS [Extent2] ON [Extent1].[TagID] = [Extent2].[ID]
LEFT OUTER JOIN [dbo].[Category] AS [Extent3] ON [Extent2].[CategoryID] = [Extent3].[ID]
) AS [Join2]
GROUP BY [K1], [K2]
) AS [GroupBy1]
其中一个联接不正确。另外,我不确定GROUP BY是否正确处理了ISNULL(它非常重要,因为我希望它与空值一起分组以及DB中带有&#34的值;其他"一起评价为一个。)
我该如何解决这个问题?或者这只是我需要回归其他东西(一个杂项或视图)的有趣场景之一?
VS2017 / C#/。NET4.7 / EF6.13 / SQLAzure
(编辑添加生成的SQL语句)
答案 0 :(得分:2)
参考导航属性生成的连接类型取决于导航属性的设置方式 - Required
- > inner join
,Optional
- > left outer join
。
由于您的关系都是可选的,因此生成的SQL使用left outer join
s。
只需插入.Where(po => po.Tag)
即可生成正确的结果。我也希望EF能够聪明地将相应的left outer join
转换为inner join
,但事实并非如此。
但是,插入中间投影然后应用非空过滤器可以解决问题:
var counts = ctx.Posts
.Select(po => new { po.Tag })
.Where(po => po.Tag != null)
.GroupBy(po => new
{
Tag = po.Tag.Name,
Category = po.Tag.Category.Name ?? "Other"
})
.Select(agg => new
{
NumberOfPosts = agg.Count(),
Tag = agg.Key.Tag,
Category = agg.Key.Category
})
.ToList();
生成所需的连接类型:
SELECT
1 AS [C1],
[GroupBy1].[A1] AS [C2],
[GroupBy1].[K1] AS [Name],
[GroupBy1].[K2] AS [C3]
FROM ( SELECT
[Filter1].[K1] AS [K1],
[Filter1].[K2] AS [K2],
COUNT([Filter1].[A1]) AS [A1]
FROM ( SELECT
[Extent2].[Name] AS [K1],
CASE WHEN ([Extent3].[Name] IS NULL) THEN N'Other' ELSE [Extent3].[Name] END AS [K2],
1 AS [A1]
FROM [dbo].[Post] AS [Extent1]
INNER JOIN [dbo].[Tag] AS [Extent2] ON [Extent1].[TagId] = [Extent2].[Id]
LEFT OUTER JOIN [dbo].[Category] AS [Extent3] ON [Extent2].[CategoryId] = [Extent3].[Id]
WHERE 1 = 1
) AS [Filter1]
GROUP BY [K1], [K2]
) AS [GroupBy1]
唯一的冗余是WHERE 1=1
,但SQL查询优化器应该能够消除它。