(単純な)クエリをSQL Server Management Studioで直接実行すると...
SELECT auftrag_prod_soll.ID
FROM auftrag_prod_soll
WHERE auftrag_prod_soll.auftrag_produktion = 51621
AND auftrag_prod_soll.prod_soll_über = 539363
ORDER BY auftrag_prod_soll.reihenfolge
...すべてがうまく高速です...
Table 'auftrag_prod_soll'. Scan count 2, logical reads 6, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 102 ms.
... SQL Serverは、2つのフィルタリング基準に基づいて適切な実行プランを選択するためです。
一方、アプリケーションがカーソルを使用して同じクエリを実行した場合...
declare @p1 int
declare @p3 int
set @p3=4
declare @p4 int
set @p4=1
declare @p5 int
set @p5=-1
exec sp_cursoropen @p1 output,N' SELECT auftrag_prod_soll.ID FROM auftrag_prod_soll WHERE auftrag_prod_soll.auftrag_produktion = 51621 AND auftrag_prod_soll.prod_soll_über = 539363 ORDER BY auftrag_prod_soll.reihenfolge',@p3 output,@p4 output,@p5 output
exec sp_cursorfetch @p1,2,0,1
exec sp_cursorclose @p1
...パフォーマンスはひどい...
Table 'auftrag_prod_soll'. Scan count 1, logical reads 1118354, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 1094 ms, elapsed time = 1231 ms.
... SQL Serverがひどい実行プランを選択するためです。
インデックスヒントを使用することで、これを回避できることを知っています。ただし、これが発生する理由を理解したいのですが。
私が試してみました:
DBCC FREEPROCCACHE
UPDATE STATISTICS auftrag_prod_soll
しかし、違いはありませんでした。
また、prod_soll_überとauftrag_produktionの2つのインデックスのヒストグラムも調べました。これらはよく分散されているため、SQL Serverはクエリが最大で数行を返すことを推測できるはずです。したがって、キールックアップと並べ替え操作はインデックススキャンよりもはるかに高速です。
また、afufrag_produktionとprod_soll_überの両方を含む非クラスター化インデックスを作成しようとしましたが、カーソルの実行プランは変更されませんでした(ただし、直接クエリがさらに高速になりました)。
以下は、関連する場合の完全なテーブル定義です。
CREATE TABLE [auftrag_prod_soll](
[auftrag_produktion] [int] NULL,
[losgrößenunabh] [smallint] NOT NULL,
[stückliste_vorh] [smallint] NOT NULL,
[erledigt] [smallint] NOT NULL,
[ext_wert_ueberst] [smallint] NOT NULL,
[ID] [int] IDENTITY(1,1) NOT NULL,
[prod_soll_über] [int] NULL,
[artikel] [int] NULL,
[gesamtmenge_soll] [float] NULL,
[produktionstext] [nvarchar](max) NULL,
[reihenfolge] [int] NULL,
[reihenfolge_druck] [int] NULL,
[infkst_unter] [int] NULL,
[ebene] [smallint] NULL,
[bezeichnung] [varchar](50) NULL,
[extern_text] [nvarchar](max) NULL,
[intern_preis] [float] NULL,
[intern_wert] [float] NULL,
[extern_preis] [float] NULL,
[extern_wert] [float] NULL,
[extern_proz] [float] NULL,
[dummyfeld] [varchar](50) NULL,
[mengeneinheit] [varchar](50) NULL,
[artikel_art] [smallint] NULL,
[s_insert] [float] NULL,
[s_update] [float] NULL,
[s_user] [varchar](255) NULL,
[preiseinheit] [float] NULL,
[memo] [nvarchar](max) NULL,
[lager_nummer] [int] NULL,
[zweitmenge] [float] NULL,
[zweit_einheit] [float] NULL,
[zweit_mengeneinh] [varchar](50) NULL,
[kst_preis1] [float] NULL,
[kst_preis2] [float] NULL,
[kst_preis3] [float] NULL,
[kst_preis4] [float] NULL,
[p_position] [int] NULL,
[zeilen_status] [int] NULL,
[fs_adresse_lief] [uniqueidentifier] NULL,
[t_artikel_stückliste] [int] NULL,
[div_text1] [varchar](255) NULL,
[div_text2] [varchar](255) NULL,
[menge_urspr] [float] NULL,
[fs_artikel_index] [uniqueidentifier] NULL,
[s_guid] [uniqueidentifier] ROWGUIDCOL NOT NULL,
[gemein_kosten] [float] NULL,
[fs_leistung] [uniqueidentifier] NULL,
[sonderlogik_ok_rech] [smallint] NOT NULL,
[sonderlogik_ok_manuell] [int] NULL,
[menge_inkl_frei] [float] NULL,
[art_einheit] [int] NULL,
[drittmenge] [float] NULL,
CONSTRAINT [PK__auftrag_prod_sol__50E5F592] PRIMARY KEY CLUSTERED ([ID] ASC)
)
CREATE NONCLUSTERED INDEX [artikel] ON [auftrag_prod_soll] ([artikel] ASC)
CREATE NONCLUSTERED INDEX [auftrag_produktion] ON [auftrag_prod_soll] ([auftrag_produktion] ASC)
CREATE NONCLUSTERED INDEX [dummyfeld] ON [auftrag_prod_soll] ([dummyfeld] ASC)
CREATE NONCLUSTERED INDEX [fs_adresse_lief] ON [auftrag_prod_soll] ([fs_adresse_lief] ASC)
CREATE NONCLUSTERED INDEX [fs_artikel_index] ON [auftrag_prod_soll] ([fs_artikel_index] ASC)
CREATE NONCLUSTERED INDEX [fs_leistung] ON [auftrag_prod_soll] ([fs_leistung] ASC)
CREATE NONCLUSTERED INDEX [lager_nummer] ON [auftrag_prod_soll] ([lager_nummer] ASC)
CREATE NONCLUSTERED INDEX [prod_soll_über] ON [auftrag_prod_soll] ([prod_soll_über] ASC)
CREATE NONCLUSTERED INDEX [reihenfolge] ON [auftrag_prod_soll] ([reihenfolge] ASC)
CREATE UNIQUE NONCLUSTERED INDEX [s_guid] ON [auftrag_prod_soll] ([s_guid] ASC)
CREATE NONCLUSTERED INDEX [s_insert] ON [auftrag_prod_soll] ([s_insert] ASC)
CREATE NONCLUSTERED INDEX [u_test] ON [auftrag_prod_soll] ([auftrag_produktion] ASC,
[prod_soll_über] ASC)
CREATE NONCLUSTERED INDEX [zeilen_status] ON [auftrag_prod_soll] ([zeilen_status] ASC)
ALTER TABLE [auftrag_prod_soll] ADD DEFAULT ((0)) FOR [losgrößenunabh]
ALTER TABLE [auftrag_prod_soll] ADD DEFAULT ((0)) FOR [stückliste_vorh]
ALTER TABLE [auftrag_prod_soll] ADD DEFAULT ((0)) FOR [erledigt]
ALTER TABLE [auftrag_prod_soll] ADD DEFAULT ((0)) FOR [ext_wert_ueberst]
ALTER TABLE [auftrag_prod_soll] ADD CONSTRAINT [DF__auftrag_p__s_gui__28A2FA0E] DEFAULT (newid()) FOR [s_guid]
ALTER TABLE [auftrag_prod_soll] ADD DEFAULT ((0)) FOR [sonderlogik_ok_rech]
カーソルが使用されている場合でも、SQL Serverがgoodクエリプランを見つけられるようにするにはどうすればよいですか?
「reihenfolge」インデックスを無効にすることで、この問題を一時的に「修正」しましたが、今後このような問題が発生しないように、なぜこれが発生するのかを理解したいと考えています。
@p3
、@p4
、および@p5
の値は、sp_cursoropen
の呼び出し後も初期値(4、1、-1)のままですが、「 reihenfolgeインデックスを削除することで問題を修正し、(1、1、0)に切り替えます。
カーソルが使用されている場合でも、SQL Serverが適切なクエリプランを見つけられるようにするにはどうすればよいですか?
文字通り:計画ガイドまたはヒントを使用します。ただし、カーソルが使用されているかどうかに関係なく、SQL Serverに最適なインデックスを提供する方がはるかに優れています。
CREATE INDEX [IX dbo.auftrag_prod_soll auftrag_produktion prod_soll_über reihenfolge]
ON dbo.auftrag_prod_soll (auftrag_produktion, prod_soll_über, reihenfolge);
これは、インデックス交差と並べ替えプランよりも優れており、スキャンインオーダーとルックアッププランよりもはるかに優れています。このインデックスを使用すると、auftrag_produktion
とprod_soll_über
の両方で等価シークが可能になると同時に、一致する行がreihenfolge
の順序で返されるようになります。
sp_cursoropen
に提供されるパラメーターは、要求されるカーソルのタイプを決定し、オプションでどのオプションを使用できるかを決定します。サーバーは、要求されたタイプとオプションが有効でないか、または(さまざまな理由により)使用できない場合、これらのオプション(したがって出力パラメーター)を変更することがあります。
提供されたコードは、サーバーが動的型カーソルとして提供する、順方向専用の読み取り専用カーソルを要求します。静的スタイルプランと動的スタイルプランのどちらを選択するかの詳細については、「 SQL Server Fast_Forward Server Cursors について」を参照してください。
問題を「修正」すると、動的計画が不可能になるため、キーセットカーソルが配信されます(動的カーソル計画ではソートできません)。
アプリケーションが必要とするカーソルオプション(たとえば、同時実行性)と、意図された使用方法を前提として、パフォーマンスに最適なタイプを指定する必要があります。すべての行をフェッチする場合、または1つの行をすばやくフェッチする計画が実際には最適ではない場合、別のタイプを指定する必要がある場合があります。 @ P3 = 8の静的。静的カーソルが確実に配信されるようにするには、0x80000(静的に受け入れ可能)を追加します。
実行プランのイメージに基づいて、SQL Serverは、キールックアップに渡す必要がある行数を過小評価する動的プランを選択するようです。
スキャンから読み取られた多数の行に注目してください。動的計画で実行できる最善の方法は、reihenfolge
インデックスを順番にスキャンすることです。 SQL Serverは統計からの値の分布を認識しますが、特定のスキャン順でのwhereを認識しません。したがって、動的計画に関連するコストを推測し、ブロッキングソート演算子を使用する計画よりもコストが低くなる可能性があります。
なぜこれが起こるのか理解したい。
これが発生する理由は、リテラル値を使用したクエリとパラメーターを使用したクエリの違いにあるようです。インデックスは「十分に分散されている」とおっしゃいましたが、まだいくつかのEdge値が存在しない可能性があり、オプティマイザーは実際の値なしにその飛躍を望んでいません。
カーソルをリテラル値で試して、カーソルの動作を確認しましたか? Management Studioでパラメーターを使用して、その動作を確認しましたか?