我有一个巨大的查询,试图提高其性能。速度缓慢的主要原因是因为我使用了很多Includes
。
我刚刚注意到我正在对Player类的两个实例进行参考比较:
Where (p => p.Player == player)
这是我的Player课:
class player
{
public string Id { get; set;}
// other properties
}
代替Where(p => p.Player.Id == player.Id)
会更快吗?
答案 0 :(得分:1)
A,您忘了告诉我们您的情况。看到标签entity-framework
,我假设您想以IQueryable
而不是AsEnumerable
的形式执行LINQ查询。
但是,假设您想知道执行时哪些代码将生成最聪明的SQL代码。
您的Player
似乎是一个实体类,与另一个实体具有一对多(或多对多)关系,例如PlayedGames
每个Player
都有零个或多个PlayedGames
,每个PlayedGame
都由一个Player
在实体框架中,您可以这样设计:
class Player
{
public int Id {get; set;}
public string Name {get; set;}
// every Player has zero or more PlayedGames
public virtual ICollection<PlayedGame> PlayedGames{get; set;}
}
class PlayedGame
{
public int Id {get; set;}
public int Score {get; set;}
// every PlayedGame was played by exactly one Player using foreign key
public int PlayerId{get; set;}
public virtual Player Player {get; set;}
}
假设您以某种方式拥有一名玩家。您可以通过多种方式获取此Player的PlayedGames。其中两个与您的代码非常相似:
Player player1 = ...
var gamesOfPlayer = dbContext.PlayedGames
.Where(game => game.Player == player1)
.ToList();
或者您可以去
var gamesOfPlayer = dbContext.PlayedGames
.Where(game => game.Player.Id == player1.Id)
.ToList();
您的查询将更加详尽,但是您会明白要点。
还有其他几种对我来说更直观的可能性:
var gamesOfPlayer = dbContext.PlayedGames
.Where(game => game.PlayerId == player1.Id)
.ToList();
var gamesOfPlayer = player.PlayedGames;
我个人将寻求最后的解决方案。对我来说,这对我来说似乎是最自然的。但是,让我们检查一下实体框架将为此编写的SQL代码。
计划变更::
尝试使用.Where(game => game.Player == player1)
执行查询时出现异常。我已经预料到,毕竟SQL不知道您何时会认为两个角色是相同的。我希望实体框架可能足够聪明,以至于用户会说Where the player has an Id equal to player1
。
所以我们必须跳过这一点。还有三个。
对于测试,我使用了Microsoft的SQL Server Profiler。对SQL进行了一些翻译,使其更具可读性
(1)使用Player.Id
.Where(game => game.Player.Id == player1.Id)
这将导致以下SQL:
exec选择 PlayedGames.Id为ID, PlayedGames.Score作为得分 来自dbo.PlayedGames作为PlayedGames 在哪里playGames.PlayerId = @ constant1
请注意,实体框架足够聪明,可以看到Player.Id
实际上是外键
(2)使用Played.Id外键
.Where(game => game.PlayerId == player1.Id)
exec Select
PlayedGames.Id as Id,
PlayedGames.Score as Score
from dbo.PlayedGames as PlayedGames 在哪里playGames.PlayerId = @ constant1
由于先前的代码已经足够聪明,可以使用外键,因此该代码导致相同的SQL也就不足为奇了
(3)使用玩家的属性PlayedGames
var gamesOfPlayer = player.PlayedGames;
exec Select
PlayedGames.Id as Id,
PlayedGames.Score as Score
from dbo.PlayedGames as PlayedGames
where playedGames.PlayerId = @constant1
实体框架未将其转换为来自Players和PLayedGames的Join!
实体框架检测到我不需要播放器的任何属性,因此再次使用外键直接进入PlayedGames表。
(实际上,因为这是我的首选方法,所以我已经知道是这种情况)。
结论
-我不确定您是否能够执行.Where(game => game.Player == player1)
。
-所有其他方法都将使用外键。它们导致相同的SQL
我个人会选择看起来最自然的代码。对于具有SQL背景的用户,这将是Where语句中具有外键的方法。对于那些在集合中进行思考的人(数据库在后台更多地是抽象的东西)将是Player.PlayedGames
的用户。
我也在代码中对此进行了测试,在其中我通过Played游戏获取了Player的一些属性。同样,这三个Where方法导致相同的SQL语句。
建议:仅当您计划更改获取的值时才使用“包含”
查询数据库最慢的部分之一是将获取的数据传输到本地进程。因此,限制传输的数据量是明智的。
如果您要使用“包含”来获取a player with his PlayedGames
,则将获得PlayedGames的所有属性,包括外键PlayerId
,或者您已经知道此值等于获取的Player.Id
。因此,如果您获取一千个玩家,每个玩家玩20场游戏,您将转移2万个您已经知道其值的外键。
除此之外,在提取之后可能还不打算使用其他属性。
所以代替:
var playersWithGames = dbContects.Players
.Include(player => player.PlayedGames)
.Where(player => ...)
.ToList();
在大多数用例中,以下内容将更加有效:
var playersWitGames = dbContext.Players
.Where(player => ...)
.Select(player => new
{ // select only the properties you plan to use
Id = player.Id,
Name = player.Name,
// not needed for this query: Birthday, emergency telephone number,
// bank account, marital status
Games = player.PlayedGames
.Where(game => ...) // if you don't need all games
.Select(game => new
{
// not sure if needed: game.Id
// certainly not needed: game.PlayerId
Date = game.Date,
Score = game.Score,
...
})
.ToList(),
});
使用获取的结果时,只要不使用您不打算使用的属性,就不会发现使用Include
或Select
有什么区别。
但是再说一遍:如果您不打算使用某个属性,则会遇到编译器错误,因此您绝不会偶然转移一个属性。
结论:仅当您计划更改获取的项目时才使用“包含”
我几乎不感谢您所有更改Player with all his PlayedGames
的计划,您都会更改Player
的属性,或者可能更改其PlayedGames
之一的属性,但是很少会一口气PlayedGames
。
根据我的经验,我很少在一对多关系中使用Include
。有时以一对零或一对的关系:“用其地址更改玩家”,尽管通常是:“更改玩家的地址”