在 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(),
   });

在使用获取的结果时,只要您不使用未计划的 属性,您将看不到使用 IncludeSelect 之间的任何区别使用。

但话又说回来:如果你使用 属性 你不打算使用你会得到一个编译器错误,所以你永远不会转移 属性 一不小心.

结论:仅当您计划更改获取的项目时才使用 Include

我很难感谢你每一次计划改变一个 Player with all his PlayedGames,你会改变 Player 的属性,或者可能改变他的一个 PlayedGames 的属性,但很少几个他的 PlayedGames 一气呵成。

根据我的经验,我很少在一对多关系中使用 Include。有时在一对零或一的关系中:"Change the Player with his Address",尽管它通常是:"Change the player's Address"