簡體   English   中英

SQL Server:未使用非聚集索引

[英]SQL Server: NonClustered index not used

我已經閱讀了很多有關索引和它們之間差異的信息。 現在我正在我的項目中進行查詢優化。 我創建了非聚集索引,該索引應在查詢執行時使用,但事實並非如此。 詳情如下:

表:

在此處輸入圖片說明

指數:

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

實體框架生成的查詢:

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'

執行計划:

在此處輸入圖片說明

缺少索引詳細信息:

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])

我知道,隨着包含AssetId和Active列以建立索引,將使用索引。

現在,為什么沒有列包含就無法正常工作?

這是另一個查詢的簡化示例,其中所有列均作為結果獲取。 (強制)索引查找使用的唯一解決方案是將索引中的所有列都包括在內,該列具有相同的估計子樹成本(顯而易見)。

這里另一個煩人的問題是排序無知。 日期列在索引中,並設置為DESCENDING。 它被完全忽略,當然,排序操作在執行計划中占據了昂貴的位置。

更新1:

正如@Jayachandran所指出的,上面的查詢中應該使用IndexSeek + KeyLookUp,但是覆蓋索引已被很好地證明,並且假定應該包括AssetId和Active列。 我同意這一點。

我正在創建UPDATE 1,以演示下面的查詢涵蓋索引的有用性。 同一張表,更大的結果集。 據我所知,索引中不應使用任何單獨的列,並且會為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'

在這種情況下,掃描和查找(使用鍵查找)的區別在於返回的行數。 容量太大,因此優化器選擇了一個更便宜的計划-只需掃描整個表即可。 這將比使用NC索引更快。

想象一下,如果您強迫它使用NC索引,並且它必須對表中40%的行進行鍵查找。 這就像執行多次foreach循環一樣。 因此,SQL選擇只掃描表是因為它比循環要快。

關於您如何處理可能包含在其他查詢中的其他列的問題,實際上有兩種選擇。 您可以創建一個包含最常用列的覆蓋索引,也可以更改主鍵以使其指向最常用的訪問路徑。 即按日期,刪除和標識列的唯一性。

另外,對主鍵使用guid會導致聚集索引和所有其他索引出現各種問題(因為PK的鍵將包含在所有其他索引中)。 Guid的隨機排序會導致在頁面中以隨機順序插入行。 由於索引是有序的,因此必須不斷拆分頁面才能計入新行。 創建一個自然增加的索引會更好,這也可能會幫助上述問題,具體取決於所寫查詢的類型。

特定查詢的理想索引是其中(1) WHERE子句中的所有字段都在索引中,以及(2) SELECT子句中的所有字段都包含在索引中的索引。 如果不滿足(1),則SQL Server將權衡訪問多個索引的成本,並選擇它認為最快的索引; 如果不滿足(2),則意味着昂貴的密鑰查找操作。 如果索引具有很高的選擇性(很少有重復值),SQL Server 可能認為值得。

在您的情況下,顯然不滿足條件(2)。 SQL Server認為,與群集索引掃描相比,“鍵查找”操作過於昂貴,因此選擇了后者。 您可以強制SQL Server使用特定的索引,但是我不知道如何使用Entity Framework。

如果此查詢必須為您快速處理,請按照SQL Server的說明創建索引。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM