[英]Why the differences in execution plan between nonclustered and clustered index in 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.