1

インデックス作成とそれらの違いについて多くのことを読みました。現在、プロジェクトでクエリの最適化に取り組んでいます。クエリの実行時に使用する必要がある非クラスター化インデックスを作成しましたが、そうではありません。以下の詳細:

テーブル:

ここに画像の説明を入力

索引:

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

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'

実行計画:

ここに画像の説明を入力

インデックスの詳細がありません:

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 列を含めると、インデックスが使用されることを認識しています。

では、列を含めないと機能しないのはなぜですか?

これは、結果としてすべての列がフェッチされる別のクエリの簡単な例です。(強制) インデックス シークの使用に対する唯一の解決策は、同じ推定サブツリー コストを持つインデックスにすべての列を含めることです (明らか)。

ここでのもう 1 つの厄介な問題は、Sort の無知です。日付列はインデックスにあり、DESCENDING に設定されています。それは完全に無視されます。もちろん、Sort 操作は実行計画で高価な場所を占めます。

更新 1:

@Jayachandran が指摘したように、上記のクエリでは IndexSeek + KeyLookUp を使用する必要がありますが、インデックスのカバーは十分に文書化されており、AssetId および Active 列を含める必要があると想定しています。私はそれに同意します。

以下のクエリでカバリング インデックスの有用性を示すために、UPDATE 1 を作成しています。同じテーブル、より大きな結果セット。私が理解できる限り、単一の列をインデックスで使用しないでください。インデックスは、日付および削除された列に対して作成されたままです。

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'
4

2 に答える 2

5

この場合のスキャンとシーク (キー検索あり)​​ との違いは、返される行数にあります。ボリュームが大きすぎるため、オプティマイザは安価なプランを選択しました - テーブル全体をスキャンするだけです。これは、NC インデックスを使用するよりも高速です。

NC インデックスの使用を強制し、テーブル内の行の 40% に対してキー ルックアップを実行する必要があると想像してください。これは、何度も実行される foreach ループのようなものです。そのため、ループよりも高速であるため、SQL はテーブルをスキャンすることを選択しました。

他のクエリに含まれる可能性のある他の列を考慮する方法についての質問に関しては、実際にはいくつかの選択肢があります。最も一般的に使用される列を含むカバリング インデックスを作成するか、主キーを変更して最も一般的なアクセス パスに向けることができます。つまり、日付別、削除済み、および一意性のための ID 列です。

別の注意として、主キーに guid を使用すると、クラスター化インデックスと他のすべてのインデックスであらゆる種類の問題が発生します (PK のキーが他のすべてのインデックスに含まれるため)。GUID のランダムな順序により、行がページにランダムな順序で挿入されます。インデックスは順序付けられているため、新しい行を考慮してページを常に分割する必要があります。自然に増加するインデックスを作成する方がはるかに優れており、これは、作成されるクエリの種類によっては、上記の問題にも役立つ可能性があります。

于 2014-09-08T13:21:00.360 に答える
1

特定のクエリの理想的なインデックスは、(1)WHERE句のすべてのフィールドがインデックスにあり、(2)SELECT句のすべてのフィールドがインデックスに含まれているものです。(1) が満たされていない場合、SQL Server は複数のインデックスにアクセスするコストを重み付けし、最も速いと思われるインデックスを選択します。(2) が満たされない場合、それは高価な Key Lookup 操作を意味します。インデックスの選択性が非常に高い (重複する値がほとんどない) 場合、 SQL Serverその価値があると判断する場合があります。

あなたの場合、条件(2)は明らかに満たされていません。SQL Server は、クラスター化インデックス スキャンに比べてキー ルックアップ操作が高すぎると判断したため、後者を選択しました。SQL Server に特定のインデックスを使用させることはできますが、Entity Framework でそれを行う方法がわかりません。

このクエリを迅速に処理する必要がある場合は、SQL Server の指示に従ってインデックスを作成してください。

于 2014-09-08T13:27:44.870 に答える