SQL Server + Entity CF不会在没有提示的情况下使用表索引

时间:2016-02-18 18:23:56

标签: sql-server entity-framework

我有一个Entity类,它生成一个相当简单的表:(混淆)

public class TableName
{
   public int TableNameId { get; set; }
   public int OtherTableId { get; set; }
   public int UserId { get; set; }
   public bool IsActive { get; set; }

   // removed other columns for brevity

   public virtual OtherTable OtherTable { get; set; }
   public virtual User User { get; set; }
}

实体自动在TableNameId上创建主键,在OtherTableId和UserId上创建索引的外键。

对于UserId和OtherTableId的每个组合,该表只有一条记录,其中IsActive为真,但记录被停用并经常更换。

记录的声明类似于:

var record =
    context.TableName
        .FirstOrDefault(x => x.UserId == userId
                          && x.OtherTableId == otherTableId
                          && x.IsActive);

我们发现,没有其他索引,拉动记录时的性能很差,因为它必须扫描UserId和OtherTableId索引才能找到IsActive = 1的记录。

为了解决这个问题,我们添加了这个索引:

create nonclustered index ix_TableName_UserId_OtherTableId_IsActive
on TableName (UserId, OtherTableId, IsActive)

但是,SQL不使用它。相反,使用此索引,它会对表的主键进行扫描!

由于Entity正在拉动整行,我理解上面的索引没有覆盖。但是,在所有其他情况下,我们已经能够添加与此类似的索引,并且它会导致索引搜索与键查找相结合并提供令人满意的性能。在这种情况下,除非我们在select语句中使用索引提示,否则它不会使用我们正在创建的索引。我们想避免这种情况,因为它需要自定义SQL,这会破坏Code First Entity的目的。

经过多次试验和错误后,SQL将自己使用的唯一索引是包含每个列的索引,因此完全覆盖。但是,我宁愿避免这种情况,因为它意味着基本上存储表两次,因为IsActive = 0,所以99%的行都是无用的。

--- 编辑 ---

对于上面的代码,我发现Entity生成以下SQL。我不知道它为什么使用子选择,但SQL优化器似乎将其过滤掉(在同一计划中运行完整的SQL或内部选择结果)。

SELECT TOP (1) 
  [Project1].[TableNameId] AS [TableNameId], 
  [Project1].[OtherTableId] AS [OtherTableId], 
  [Project1].[UserId] AS [UserId],     
  [Project1].[IsActive] AS [IsActive]    
  FROM ( SELECT 
      [Extent1].[TableNameId] AS [TableNameId], 
      [Extent1].[OtherTableId] AS [OtherTableId], 
      [Extent1].[UserId] AS [UserId],         
      [Extent1].[IsActive] AS [IsActive]        
      FROM [dbo].[TableName] AS [Extent1]
      WHERE ([Extent1].[UserId] = <userId>)
        AND ([Extent1].[OtherTableId] = <otherTableId>)
        AND ([Extent1].[IsActive] = 1)
  )  AS [Project1]

0 个答案:

没有答案