简体   繁体   English

SQL Server:未使用非聚集索引

[英]SQL Server: NonClustered index not used

I have read a lot about indexing and differences between them. 我已经阅读了很多有关索引和它们之间差异的信息。 Now i am working on query optimization in my project. 现在我正在我的项目中进行查询优化。 I have created nonclustered index, that should be used on query execution, but that is not the case. 我创建了非聚集索引,该索引应在查询执行时使用,但事实并非如此。 Details below: 详情如下:

Table: 表:

在此处输入图片说明

Index: 指数:

CREATE NONCLUSTERED INDEX [_IXProcedure_Deleted_Date] ON [por].[DailyAsset]
(
    [Deleted] ASC,
    [Date] DESC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO

Query generated by Entity Framework: 实体框架生成的查询:

exec sp_executesql N'SELECT 
[Project1].[C1] AS [C1], 
[Project1].[AssetId] AS [AssetId], 
[Project1].[Active] AS [Active], 
[Project1].[Date] AS [Date]
FROM ( SELECT 
    [Extent1].[AssetId] AS [AssetId], 
    [Extent1].[Active] AS [Active], 
    [Extent1].[Date] AS [Date], 
    1 AS [C1]
    FROM [por].[DailyAsset] AS [Extent1]
    WHERE (0 = [Extent1].[Deleted]) AND ([Extent1].[Date] < @p__linq__0)
)  AS [Project1]
ORDER BY [Project1].[Date] DESC',N'@p__linq__0 datetime2(7)',@p__linq__0='2014-05-01 00:00:00'

Execution plan: 执行计划:

在此处输入图片说明

Missing index details: 缺少索引详细信息:

The Query Processor estimates that implementing the following index could improve the query cost by 23.8027%.


CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>]
ON [por].[DailyAsset] ([Deleted],[Date])
INCLUDE ([AssetId],[Active])

I am aware that with inclusion of AssetId and Active columns to index, index will be used. 我知道,随着包含AssetId和Active列以建立索引,将使用索引。

Now, why it is not working without column inclusion? 现在,为什么没有列包含就无法正常工作?

This is simplified example of another queries, where all columns are fetched as a result. 这是另一个查询的简化示例,其中所有列均作为结果获取。 The only solution to (force) index seek usage, is to include all columns in index, which has the same Estimated Subtree cost (obvious). (强制)索引查找使用的唯一解决方案是将索引中的所有列都包括在内,该列具有相同的估计子树成本(显而易见)。

Another annoying issue here is Sort ignorance. 这里另一个烦人的问题是排序无知。 Date columns is in index and set to DESCENDING. 日期列在索引中,并设置为DESCENDING。 It is completly ignored, and ofcourse, Sort operations takes expensive place in execution plan. 它被完全忽略,当然,排序操作在执行计划中占据了昂贵的位置。

UPDATE 1: 更新1:

As @Jayachandran pointed out, IndexSeek + KeyLookUp should be used in query above, but covering index is well documented, and it assumes that AssetId and Active columns should be included. 正如@Jayachandran所指出的,上面的查询中应该使用IndexSeek + KeyLookUp,但是覆盖索引已被很好地证明,并且假定应该包括AssetId和Active列。 I agree with that. 我同意这一点。

I am creating UPDATE 1 to demonstrate covering index usefulness in query below. 我正在创建UPDATE 1,以演示下面的查询涵盖索引的有用性。 Same table, larger result set. 同一张表,更大的结果集。 As far as i can figure it out, not a single columns should not be used in index, and index stays created for Date and Deleted columns. 据我所知,索引中不应使用任何单独的列,并且会为Date和Deleted列创建索引。

exec sp_executesql N'SELECT 
[Project1].[DailyAssetId] AS [DailyAssetId], 
[Project1].[AssetId] AS [AssetId], 
[Project1].[CreatedByUserId] AS [CreatedByUserId], 
[Project1].[UpdatedByUserId] AS [UpdatedByUserId], 
[Project1].[TimeCreated] AS [TimeCreated], 
[Project1].[TimeUpdated] AS [TimeUpdated], 
[Project1].[Deleted] AS [Deleted], 
[Project1].[TimeDeleted] AS [TimeDeleted], 
[Project1].[DeletedByUserId] AS [DeletedByUserId], 
[Project1].[Active] AS [Active], 
[Project1].[Date] AS [Date], 
[Project1].[Quantity] AS [Quantity], 
[Project1].[TotalBookValue] AS [TotalBookValue], 
[Project1].[CostPrice] AS [CostPrice], 
[Project1].[CostValue] AS [CostValue], 
[Project1].[FairPrice] AS [FairPrice], 
[Project1].[FairValue] AS [FairValue], 
[Project1].[UnsettledQuantity] AS [UnsettledQuantity], 
[Project1].[UnsettledValue] AS [UnsettledValue], 
[Project1].[SettlementDate] AS [SettlementDate], 
[Project1].[EffectiveDate] AS [EffectiveDate], 
[Project1].[PortfolioId] AS [PortfolioId]
FROM ( SELECT 
    [Extent1].[DailyAssetId] AS [DailyAssetId], 
    [Extent1].[AssetId] AS [AssetId], 
    [Extent1].[CreatedByUserId] AS [CreatedByUserId], 
    [Extent1].[UpdatedByUserId] AS [UpdatedByUserId], 
    [Extent1].[TimeCreated] AS [TimeCreated], 
    [Extent1].[TimeUpdated] AS [TimeUpdated], 
    [Extent1].[Deleted] AS [Deleted], 
    [Extent1].[TimeDeleted] AS [TimeDeleted], 
    [Extent1].[DeletedByUserId] AS [DeletedByUserId], 
    [Extent1].[Active] AS [Active], 
    [Extent1].[Date] AS [Date], 
    [Extent1].[Quantity] AS [Quantity], 
    [Extent1].[TotalBookValue] AS [TotalBookValue], 
    [Extent1].[CostPrice] AS [CostPrice], 
    [Extent1].[CostValue] AS [CostValue], 
    [Extent1].[FairPrice] AS [FairPrice], 
    [Extent1].[FairValue] AS [FairValue], 
    [Extent1].[UnsettledQuantity] AS [UnsettledQuantity], 
    [Extent1].[UnsettledValue] AS [UnsettledValue], 
    [Extent1].[SettlementDate] AS [SettlementDate], 
    [Extent1].[EffectiveDate] AS [EffectiveDate], 
    [Extent1].[PortfolioId] AS [PortfolioId]
    FROM [por].[DailyAsset] AS [Extent1]
    WHERE (0 = [Extent1].[Deleted]) AND ([Extent1].[Date] < @p__linq__0)
)  AS [Project1]
ORDER BY [Project1].[Date] DESC',N'@p__linq__0 datetime2(7)',@p__linq__0='2014-05-01 00:00:00'

The difference with a scan and a seek (with key lookup) in this case is because of the number of the rows being returned. 在这种情况下,扫描和查找(使用键查找)的区别在于返回的行数。 The volume is too great and so the optimizer has chosen a cheaper plan - just scan the whole table. 容量太大,因此优化器选择了一个更便宜的计划-只需扫描整个表即可。 This will be faster than using the NC index. 这将比使用NC索引更快。

Imagine if you had forced it to use the NC index and it had to do a key lookup for 40% of the rows in the table. 想象一下,如果您强迫它使用NC索引,并且它必须对表中40%的行进行键查找。 This would be like a foreach loop executing many times. 这就像执行多次foreach循环一样。 So SQL has chosen to just scan the table because it would be faster than the loop. 因此,SQL选择只扫描表是因为它比循环要快。

Regarding your question about how to account for other columns that might be included in other queries, there are really a couple of choices. 关于您如何处理可能包含在其他查询中的其他列的问题,实际上有两种选择。 You could create a covering index that includes the most commonly used columns or you could change the primary key to orient it towards your most common access path. 您可以创建一个包含最常用列的覆盖索引,也可以更改主键以使其指向最常用的访问路径。 ie by date, deleted and an identity column for uniqueness. 即按日期,删除和标识列的唯一性。

On another note, using a guid for a primary key causes all sorts of problems with your clustered index and all other indexes (because the key for the PK will be included in all other indexes). 另外,对主键使用guid会导致聚集索引和所有其他索引出现各种问题(因为PK的键将包含在所有其他索引中)。 The random ordering of guids causes rows to be inserted in random order in pages. Guid的随机排序会导致在页面中以随机顺序插入行。 Because the index is ordered, pages must constantly be split in order to account for new rows. 由于索引是有序的,因此必须不断拆分页面才能计入新行。 It would be far better to create an index that naturally increments and this would likely help the problem above as well, depending on the types of queries that are written. 创建一个自然增加的索引会更好,这也可能会帮助上述问题,具体取决于所写查询的类型。

The ideal index for a specific query is one where (1) all the fields in the WHERE clause are in the index, and (2) all the fields in the SELECT clause are included in the index. 特定查询的理想索引是其中(1) WHERE子句中的所有字段都在索引中,以及(2) SELECT子句中的所有字段都包含在索引中的索引。 If (1) is not met, SQL Server will weight the cost of accessing multiple indexes and pick the one it think is fastest; 如果不满足(1),则SQL Server将权衡访问多个索引的成本,并选择它认为最快的索引; if (2) is not met, that means an expensive Key Lookup operation. 如果不满足(2),则意味着昂贵的密钥查找操作。 SQL Server may think it's worth it if the index has very high selectivity (few duplicate values). 如果索引具有很高的选择性(很少有重复值),SQL Server 可能认为值得。

In your case, condition (2) is clearly not met. 在您的情况下,显然不满足条件(2)。 SQL Server thinks that the Key Lookup operation is too expensive compared to a clustered index scan so it chose the later. SQL Server认为,与群集索引扫描相比,“键查找”操作过于昂贵,因此选择了后者。 You can force SQL Server to use a particular index, but I don't know how to do that with Entity Framework. 您可以强制SQL Server使用特定的索引,但是我不知道如何使用Entity Framework。

If this query must be speedy for you, create the index as SQL Server says. 如果此查询必须为您快速处理,请按照SQL Server的说明创建索引。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM