我知道有一些关于此的问题,大多数与已解决的旧问题或多个表有关。在我看到的任何其他“左外连接”问题中都没有涉及此问题,我在同一查询中得到INNER JOIN
和LEFT OUTER JOIN
到同一个表。
表格大纲:
Users: id (PK)
Name (VARCHAR)
ProfileImageUri (VARCHAR)
Locations: id (PK)
LocationBPNTips: id (PK)
TipText (VARCHAR)
CreatedAt (Datetime)
UserId (int) (FK to User.id, navigation property is called User)
LocationId (int) (FK to Location.id)
(还有更多,但不相关:))
在我的场景中,我通过投影对引用的表执行查询,我得到一个额外的左外连接,这是我运行的(注释的部分与问题无关,评论为了更清洁的SQL,EF做正确的排序(甚至比我想象的更好:))):
LocationBPNTips
.Where(t => t.LocationId == 33)
//.OrderByDescending(t => intList.Contains(t.UserId))
//.ThenByDescending(t => t.CreatedAt)
.Select(tip => new LocationTipOutput
{
CreatedAt = tip.CreatedAt,
Text = tip.TipText,
LocationId = tip.LocationId,
OwnerName = tip.User.Name,
OwnerPhoto = tip.User.ProfileImageUri
}).ToList();
这就是生成的SQL
SELECT
[Extent1].[LocationId] AS [LocationId],
[Extent1].[CreatedAt] AS [CreatedAt],
[Extent1].[TipText] AS [TipText],
[Extent2].[Name] AS [Name],
[Extent3].[ProfileImageUri] AS [ProfileImageUri]
FROM [dbo].[LocationBPNTips] AS [Extent1]
INNER JOIN [dbo].[Users] AS [Extent2] ON [Extent1].[UserId] = [Extent2].[Id]
LEFT OUTER JOIN [dbo].[Users] AS [Extent3] ON [Extent1].[UserId] = [Extent3].[Id]
WHERE 33 = [Extent1].[LocationId]
如您所见,LEFT OUTER JOIN
是在INNER JOIN
我认为最佳代码将是(注意,我手动将Extent3重命名为Extent2,并添加了注释。这不是由EF生成的!) - 使用我当前的数据,运行速度提高约22%(排序,这个%应该更高,没有排序),因为不需要额外的连接..
SELECT
[Extent1].[LocationId] AS [LocationId],
[Extent1].[CreatedAt] AS [CreatedAt],
[Extent1].[TipText] AS [TipText],
[Extent2].[Name] AS [Name],
[Extent2].[ProfileImageUri] AS [ProfileImageUri]
FROM [dbo].[LocationBPNTips] AS [Extent1]
INNER JOIN [dbo].[Users] AS [Extent2] ON [Extent1].[UserId] = [Extent2].[Id]
--LEFT OUTER JOIN [dbo].[Users] AS [Extent3] ON [Extent1].[UserId] = [Extent3].[Id]
WHERE 33 = [Extent1].[LocationId]
我尝试过的不同查询(投影是这些中的匿名类型):
LocationBPNTips
.Where(t => t.LocationId == 33)
//.OrderByDescending(t => intList.Contains(t.UserId))
//.ThenByDescending(t => t.CreatedAt)
.Select(tip => new
{
CreatedAt = tip.CreatedAt,
Text = tip.TipText,
LocationId = tip.LocationId,
OwnerName = tip.User,
OwnerPhoto = tip.User
}).ToList()
SQL输出搞砸了,它以与上面相同的格式选择整个用户表两次,内部然后左外部。我认为我在理论上可以看到为什么会发生这种情况,因为我要求两次数据 - 尽管它可以在内存中完成,而不是由SQL进行额外的连接 - 但在我的情况下我没有要求数据两次,我只询问一次不同的列。我做了这个测试,看看双连接是否一致。
我也尝试过跑步:
LocationBPNTips
.Where(t => t.LocationId == 33)
.Select(tip => new
{
CreatedAt = tip.CreatedAt,
Text = tip.TipText,
LocationId = tip.LocationId,
OwnerName = tip.User.Name
}).ToList()
这个按预期返回了干净的单内连接,但这不是我想做的事情
所以问题是:这是一个错误吗?我错误地使用了EF吗?
答案 0 :(得分:0)
我已经看过类似的问题了。我们可以称之为bug或功能。简单的EF查询生成远非理想。 ADO.NET团队修复了每个主要版本的一些问题。我目前没有安装June CTP 2011以验证它是否也出现在下一版本的第一个预览中。
编辑:
根据this answer类似的问题在2011年6月CTP修复。