T-SQL のパフォーマンスの問題
適用対象: SQL Server Azure SQL Database Azure SQL Managed Instance Microsoft Fabric SQL Database
データベース プロジェクトで T-SQL コードを分析すると、1 つまたは複数の警告がパフォーマンスの問題として分類される可能性があります。 次の状況を回避するには、パフォーマンスの問題に対処する必要があります:
- テーブル スキャンは、コードが実行されるときに発生します。
一般に、テーブルにデータが少なく、スキャンによってパフォーマンスが大幅に低下しないのであれば、パフォーマンスの問題を抑制できます。
指定されたルールでは、次のパフォーマンスの問題が特定されます:
- SR0004: IN 述語のテスト式としてインデックスがない列を使用しないようにする
- SR0005: LIKE 述語において "%" で始まるパターンを使用しないようにする
- SR0006: 列参照を比較演算子の一方の側に移動して、列インデックスを使用する
- SR0007: 式の null 許容列に ISNULL(column, default_value) を使用する
- SR0015: WHERE 述語から決定論的関数呼び出しを抽出する
SR0004: IN 述語のテスト式としてインデックスがない列を使用しないようにする
IN 述語の一部としてインデックスが作成されていない 1 つまたは複数の列を参照する WHERE 句を使用すると、テーブル スキャンが実行されます。 テーブル スキャンを使用すると、パフォーマンスが低下します。
違反の修正方法
この問題を解決するには、次のいずれかの変更を行う必要があります:
- インデックスを持つ列のみを参照するように IN 述語を変更します。
- IN 述語が参照していて、まだインデックスがない列にインデックスを追加します。
例
この例では、単純な SELECT ステートメントが、インデックスを持たない列 [c1] を参照しています。 2 番目のステートメントでは、この警告を解決するために追加できるインデックスを定義します。
CREATE PROCEDURE [dbo].[Procedure3WithWarnings]
AS
SELECT [Comment]
FROM [dbo].[Table2]
WHERE [c1] IN (1, 2, 3)
CREATE INDEX [IX_Table2_C1]
ON [dbo].[Table2] (c1);
SR0005: LIKE 述語において "%" で始まるパターンを使用しないようにする
'%pattern string' などの LIKE 述語を含む WHERE 句を使用して、列内の任意の場所で発生する可能性のあるテキストを検索すると、テーブル スキャンが発生する可能性があります。
違反の修正方法
この問題を解決するには、ワイルドカード (%) ではない文字で始まる検索文字列を変更するか、フルテキスト インデックスを作成する必要があります。
例
最初の例では、検索文字列がワイルドカード文字で始まるため、SELECT ステートメントによってテーブル スキャンが実行されます。 2 番目の例では、検索文字列がワイルドカード文字で始まらないため、ステートメントによってインデックスシークが発生します。 インデックス検索では、WHERE 句に一致する行のみが取得されます。
SELECT [dbo].[Table2].[ID], [dbo].[Table2].[c1], [dbo].[Table2].[c2], [dbo].[Table2].[c3], [dbo].[Table2].[Comment]
FROM dbo.[Table2]
WHERE Comment LIKE '%pples'
SELECT [dbo].[Table2].[ID], [dbo].[Table2].[c1], [dbo].[Table2].[c2], [dbo].[Table2].[c3], [dbo].[Table2].[Comment]
FROM dbo.[Table2]
WHERE Comment LIKE 'A%'
SR0006: 列参照を比較演算子の一方の側に移動して、列インデックスを使用する
列参照を含む式を比較する場合、コードによってテーブル スキャンが発生する可能性があります。
違反の修正方法
この問題を解決するには、式内ではなく、比較演算子の一方の側に列参照が単独で表示されるように、比較をやり直す必要があります。 比較演算子の一方の側で列参照を持つコードを単独で実行すると、SQL Server は列インデックスを使用でき、テーブル スキャンは実行されません。
例
最初のプロシージャでは、WHERE 句に比較の一部として式の列 [c1] が含まれています。 2 番目の手順では、比較結果は同じですが、テーブル スキャンは必要ありません。
CREATE PROCEDURE [dbo].[Procedure3WithWarnings]
@param1 int
AS
SELECT [c1], [c2], [c3], [Comment]
FROM [dbo].[Table2]
WHERE ([c1] + 5 > @param1)
CREATE PROCEDURE [dbo].[Procedure3Fixed]
@param1 int
AS
SELECT [c1], [c2], [c3], [Comment]
FROM [dbo].[Table2]
WHERE ([c1] > (@param1 - 5))
SR0007: 式の null 許容列に ISNULL(column, default_value) を使用する
コードで 2 つの NULL
値または NULL
値を他の値と比較すると、不明な結果が返されます。
違反の修正方法
ISNULL
関数に NULL
値を含めることができる各列をラップして、比較式の NULL
値を処理する方法を明示的に指定する必要があります。
例
この例では、単純なテーブル定義と 2 つのストアド プロシージャを示します。 テーブルには、NULL
値を含むことができる列 (c2
) が含まれています。 最初のプロシージャ (ProcedureWithWarning
) は、c2
を定数値と比較します。 2 番目の手順では、ISNULL
関数の呼び出しで c2
をラップすることで問題を修正します。
CREATE TABLE [dbo].[Table1]
(
[ID] INT NOT NULL IDENTITY(0, 1),
[c1] INT NOT NULL PRIMARY KEY,
[c2] INT
)
ON [PRIMARY]
CREATE PROCEDURE [dbo].[ProcedureWithWarning]
AS
BEGIN
SELECT COUNT(*) FROM [dbo].[Table1]
WHERE [c2] > 2;
END
CREATE PROCEDURE [dbo].[ProcedureFixed]
AS
BEGIN
SELECT COUNT(*) FROM [dbo].[Table1]
WHERE ISNULL([c2],0) > 2;
END
SR0015: WHERE 述語から決定論的関数呼び出しを抽出する
WHERE 述語では、その値が選択したデータに依存しない場合、関数呼び出しは決定論的です。 このような呼び出しにより、不要なテーブル スキャンが発生し、データベースのパフォーマンスが低下する可能性があります。
違反の修正方法
この問題を解決するには、WHERE 述語で使用する変数に呼び出しの結果を割り当てることができます。
例
最初の例では、ストアド プロシージャは WHERE 述語に決定論的関数呼び出し (ABS(@param1)
) を含みます。 2 番目の例では、一時変数が呼び出しの結果を保持しています。
CREATE PROCEDURE [dbo].[Procedure2WithWarning]
@param1 INT = 0,
AS
BEGIN
SELECT [c1], [c2], [c3], [SmallString]
FROM [dbo].[Table1]
WHERE [c2] > ABS(@param1)
END
CREATE PROCEDURE [dbo].[Procedure2Fixed]
@param1 INT = 0,
AS
BEGIN
DECLARE @AbsOfParam1 INT
SET @AbsOfParam1 = ABS(@param1)
SELECT [c1], [c2], [c3], [SmallString]
FROM [dbo].[Table1]
WHERE [c2] > @AbsOfParam1
END