9

SELECTこれら 2 つの の実行計画とパフォーマンスは同じであると予想されます。の先頭にワイルドカードがあるためLIKE、インデックス スキャンが必要です。これを実行して計画を確認すると、最初の計画はSELECT(スキャンを使用して) 期待どおりに動作します。しかし、2 番目のSELECTプランはインデックス シークを示しており、20 倍速く実行されます。

コード:

-- Uses index scan, as expected:
SELECT 1
    FROM AccountAction
    WHERE AccountNumber LIKE '%441025586401'

-- Uses index seek somehow, and runs much faster:
declare @empty VARCHAR(30) = ''
SELECT 1
    FROM AccountAction
    WHERE AccountNumber LIKE '%441025586401' + @empty

質問:

パターンがワイルドカードで始まる場合、SQL Server はどのようにインデックス シークを使用しますか?

おまけの質問:

空の文字列を連結すると実行計画が変更/改善されるのはなぜですか?

詳細:

  • に非クラスター化インデックスがありますAccounts.AccountNumber
  • 他のインデックスがありますが、シークとスキャンの両方がこのインデックス上にあります。
  • 列はAccounts.AccountNumberヌル可能ですvarchar(30)
  • サーバーは SQL Server 2012 です

テーブルとインデックスの定義:

CREATE TABLE [updatable].[AccountAction](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [AccountNumber] [varchar](30) NULL,
    [Utility] [varchar](9) NOT NULL,
    [SomeData1] [varchar](10) NOT NULL,
    [SomeData2] [varchar](200) NULL,
    [SomeData3] [money] NULL,
    --...
    [Created] [datetime] NULL,
 CONSTRAINT [PK_Account] PRIMARY KEY NONCLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]


CREATE NONCLUSTERED INDEX [IX_updatable_AccountAction_AccountNumber_UtilityCode_ActionTypeCd] ON [updatable].[AccountAction]
(
    [AccountNumber] ASC,
    [Utility] ASC
)
INCLUDE ([SomeData1], [SomeData2], [SomeData3]) 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]


CREATE CLUSTERED INDEX [CIX_Account] ON [updatable].[AccountAction]
(
    [Created] ASC
)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]

注: 2 つのクエリの実際の実行計画は次のとおりです。質問を単純にしようとしたため、オブジェクトの名前は上記のコードとは少し異なります。

2 つの実行計画。

4

1 に答える 1

8

これらのテスト (データベース AdventureWorks2008R2) は、何が起こるかを示しています。

SET NOCOUNT ON;
SET STATISTICS IO ON;

PRINT 'Test #1';
SELECT  p.BusinessEntityID, p.LastName
FROM    Person.Person p
WHERE   p.LastName LIKE '%be%';

PRINT 'Test #2';
DECLARE @Pattern NVARCHAR(50);
SET @Pattern=N'%be%';
SELECT  p.BusinessEntityID, p.LastName
FROM    Person.Person p
WHERE   p.LastName LIKE @Pattern;

SET STATISTICS IO OFF;
SET NOCOUNT OFF;

結果:

Test #1
Table 'Person'. Scan count 1, logical reads 106
Test #2
Table 'Person'. Scan count 1, logical reads 106

の結果SET STATISTICS IOは、LIO が同じであることを示しています。しかし、実行計画はかなり異なります。 ここに画像の説明を入力

最初のテストでは、SQL Server はIndex Scan明示的な を使用しますが、2 番目のテストでは、SQL Server は であるIndex Seekを使用しますIndex Seek - range scan。最後のケースでは、SQL Server はCompute Scalar演算子を使用してこれらの値を生成します

[Expr1005] = Scalar Operator(LikeRangeStart([@Pattern])), 
[Expr1006] = Scalar Operator(LikeRangeEnd([@Pattern])), 
[Expr1007] = Scalar Operator(LikeRangeInfo([@Pattern]))

そして、Index Seekオペレーターは( ) と別の最適化されていない( Seek Predicate) に対して (最適化された) を使用します。range scanLastName > LikeRangeStart AND LastName < LikeRangeEndPredicateLastName LIKE @pattern

LIKE '%...' でインデックスを検索するにはどうすればよいですか?

私の答え:それは「本物」ではありませんIndex Seek。このIndex Seek - range scan場合、 と同じ性能を持つIndex Scanです。

Index SeekIndex Scan(同様の議論) の違いも参照してください.

編集1:(OPTION(RECOMPILE)アーロンの推奨事項を参照してください)の実行計画は、 Index Scan(の代わりにIndex Seek)も示しています: ここに画像の説明を入力

于 2013-08-05T20:07:47.173 に答える