在 linq 中,通过引用比较还是通过字符串比较更快?
In linq, is comparing by reference or string faster?
我有一个庞大的查询,我正试图提高其性能。它慢的主要原因是因为我使用了很多Includes
。
我刚刚注意到我正在对我的 Player 的两个实例进行参考比较 class:
Where (p => p.Player == player)
这是我的播放器 class:
class player
{
public string Id { get; set;}
// other properties
}
改为 Where(p => p.Player.Id == player.Id)
会更快吗?
唉,你忘了告诉我们你的背景。看到你的标签 entity-framework
我假设你想以 IQueryable
的形式执行 LINQ 查询,而不是 AsEnumerable
.
但我们假设您想知道执行时哪些代码会产生最聪明的 SQL 代码。
您的 Player
似乎是一个实体 class 与另一个实体存在一对多(或多对多)关系,例如 PlayedGames
每个Player
有零个或多个 PlayedGames
,每个 PlayedGame
正好被一个 Player
玩过
在 entity framework 中,您可以这样设计:
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 代码,entity framework 将使用它。
计划变更::
当我尝试使用 .Where(game => game.Player == player1)
执行查询时出现异常。我已经预料到,毕竟SQL不知道你什么时候会认为两个玩家是一样的。我希望也许 entity framework 足够聪明,认为用户的意思是 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:
执行 Select
PlayedGames.Id 作为身份证,
PlayedGames.Score 作为分数
来自 dbo.PlayedGames 作为 PlayedGames
其中 playedGames.PlayerId = @constant1
请注意 entity framework 足够聪明,可以看出 Player.Id
实际上是外键
(2) 使用外键Played.Id
.Where(game => game.PlayerId == player1.Id)
exec Select
PlayedGames.Id as Id,
PlayedGames.Score as Score
来自 dbo.PlayedGames 作为 PlayedGames
其中 playedGames.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
Entity Framework 没有将其转化为玩家和 PLayedGames 的加入!
Entity Framework 检测到我不需要 Player 的任何属性,因此再次使用外键直接进入 PlayedGames table。
(其实,这是我的首选方法,我早就知道会是这样)。
结论
- 我不确定你是否能够执行 .Where(game => game.Player == player1)
。
- 所有其他方法都将使用外键。它们导致相同的 SQL
就我个人而言,我会选择看起来最自然的代码。对于具有 SQL 背景的人,这将是 Where 语句中带有外键的方法。对于那些在集合中思考的人(数据库在后台更像是一个抽象的东西),它将是 Player.PlayedGames
.
的用户
我还在代码中对此进行了测试,我在代码中获取了玩家玩过的游戏的一些属性。同样,三个 Where 方法导致相同的 SQL 语句。
使用 Select 而不是包含
建议:只有在您打算更改获取的值时才使用包含
查询数据库较慢的部分之一是将获取的数据传输到本地进程。因此,限制传输的数据量是明智的。
如果您使用 Include 获取 a player with his PlayedGames
,您将获得 PlayedGames 的所有属性,包括外键 PlayerId
,或者您已经知道该值等于获取的 Player.Id
.因此,如果您获取 1000 个玩家,每个玩家玩过 20 场游戏,您将转移 20,000 个您已经知道其价值的外键。
除此之外,您可能不打算在提取后使用其他属性。
所以代替:
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
之间的任何区别使用。
但话又说回来:如果你使用 属性 你不打算使用你会得到一个编译器错误,所以你永远不会转移 属性 一不小心.
结论:仅当您计划更改获取的项目时才使用 Include
我很难感谢你每一次计划改变一个 Player with all his PlayedGames
,你会改变 Player
的属性,或者可能改变他的一个 PlayedGames
的属性,但很少几个他的 PlayedGames
一气呵成。
根据我的经验,我很少在一对多关系中使用 Include
。有时在一对零或一的关系中:"Change the Player with his Address",尽管它通常是:"Change the player's Address"
我有一个庞大的查询,我正试图提高其性能。它慢的主要原因是因为我使用了很多Includes
。
我刚刚注意到我正在对我的 Player 的两个实例进行参考比较 class:
Where (p => p.Player == player)
这是我的播放器 class:
class player
{
public string Id { get; set;}
// other properties
}
改为 Where(p => p.Player.Id == player.Id)
会更快吗?
唉,你忘了告诉我们你的背景。看到你的标签 entity-framework
我假设你想以 IQueryable
的形式执行 LINQ 查询,而不是 AsEnumerable
.
但我们假设您想知道执行时哪些代码会产生最聪明的 SQL 代码。
您的 Player
似乎是一个实体 class 与另一个实体存在一对多(或多对多)关系,例如 PlayedGames
每个Player
有零个或多个 PlayedGames
,每个 PlayedGame
正好被一个 Player
在 entity framework 中,您可以这样设计:
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 代码,entity framework 将使用它。
计划变更::
当我尝试使用 .Where(game => game.Player == player1)
执行查询时出现异常。我已经预料到,毕竟SQL不知道你什么时候会认为两个玩家是一样的。我希望也许 entity framework 足够聪明,认为用户的意思是 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:
执行 Select PlayedGames.Id 作为身份证, PlayedGames.Score 作为分数 来自 dbo.PlayedGames 作为 PlayedGames 其中 playedGames.PlayerId = @constant1
请注意 entity framework 足够聪明,可以看出 Player.Id
实际上是外键
(2) 使用外键Played.Id
.Where(game => game.PlayerId == player1.Id)
exec Select
PlayedGames.Id as Id,
PlayedGames.Score as Score
来自 dbo.PlayedGames 作为 PlayedGames 其中 playedGames.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
Entity Framework 没有将其转化为玩家和 PLayedGames 的加入!
Entity Framework 检测到我不需要 Player 的任何属性,因此再次使用外键直接进入 PlayedGames table。
(其实,这是我的首选方法,我早就知道会是这样)。
结论
- 我不确定你是否能够执行 .Where(game => game.Player == player1)
。
- 所有其他方法都将使用外键。它们导致相同的 SQL
就我个人而言,我会选择看起来最自然的代码。对于具有 SQL 背景的人,这将是 Where 语句中带有外键的方法。对于那些在集合中思考的人(数据库在后台更像是一个抽象的东西),它将是 Player.PlayedGames
.
我还在代码中对此进行了测试,我在代码中获取了玩家玩过的游戏的一些属性。同样,三个 Where 方法导致相同的 SQL 语句。
使用 Select 而不是包含
建议:只有在您打算更改获取的值时才使用包含
查询数据库较慢的部分之一是将获取的数据传输到本地进程。因此,限制传输的数据量是明智的。
如果您使用 Include 获取 a player with his PlayedGames
,您将获得 PlayedGames 的所有属性,包括外键 PlayerId
,或者您已经知道该值等于获取的 Player.Id
.因此,如果您获取 1000 个玩家,每个玩家玩过 20 场游戏,您将转移 20,000 个您已经知道其价值的外键。
除此之外,您可能不打算在提取后使用其他属性。
所以代替:
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
之间的任何区别使用。
但话又说回来:如果你使用 属性 你不打算使用你会得到一个编译器错误,所以你永远不会转移 属性 一不小心.
结论:仅当您计划更改获取的项目时才使用 Include
我很难感谢你每一次计划改变一个 Player with all his PlayedGames
,你会改变 Player
的属性,或者可能改变他的一个 PlayedGames
的属性,但很少几个他的 PlayedGames
一气呵成。
根据我的经验,我很少在一对多关系中使用 Include
。有时在一对零或一的关系中:"Change the Player with his Address",尽管它通常是:"Change the player's Address"