在linq中,通过引用或字符串进行比较的速度更快吗?

时间:2018-07-24 22:40:28

标签: c# entity-framework linq .net-core-2.0

我有一个巨大的查询,试图提高其性能。速度缓慢的主要原因是因为我使用了很多Includes

我刚刚注意到我正在对Player类的两个实例进行参考比较:

Where (p => p.Player == player)

这是我的Player课:

class player
{
    public string Id { get; set;}
    // other properties
}

代替Where(p => p.Player.Id == player.Id)会更快吗?

1 个答案:

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

使用获取的结果时,只要不使用您不打算使用的属性,就不会发现使用IncludeSelect有什么区别。

但是再说一遍:如果您不打算使用某个属性,则会遇到编译器错误,因此您绝不会偶然转移一个属性

结论:仅当您计划更改获取的项目时才使用“包含”

我几乎不感谢您所有更改Player with all his PlayedGames的计划,您都会更改Player的属性,或者可能更改其PlayedGames之一的属性,但是很少会一口气PlayedGames

根据我的经验,我很少在一对多关系中使用Include。有时以一对零或一对的关系:“用其地址更改玩家”,尽管通常是:“更改玩家的地址”