web-dev-qa-db-ja.com

@parameter IS NULLはクエリの実行を遅くできますか?

オプション1:

CREATE PROCEDURE [dbo].[GetStudents]
@MinimumAge int = NULL
AS

select * from Students s where @MinimumAge is null or s.Age >= @MinimumAge

オプション2:

CREATE PROCEDURE [dbo].[GetStudents]
@MinimumAge int = NULL
AS

IF @MinimumAge IS NULL
BEGIN
    select * from Students s
END
ELSE
BEGIN
    select * from Students s where s.Age >= @MinimumAge
END

オプション1には追加のWHERE句があるため、オプション2よりも遅くなりますか?それともSQL Serverがそれを処理しますか?

遅くならない限り、コードを複製する必要がないので、オプション1が良いと思います。元のプロシージャには多くのコード行があります。ですから、唯一の良いオプションでない限り、1つの条件ですべてのコードを複製したくありません。

age列にインデックスがあり、nullは許可されていません。

問題は、Actual SPはコードの行数が多いためです。つまり、唯一の適切なオプションでない限り、where条件を1つだけ追加するためにすべてのコードを複製したくありません。

2
Jay Shah

コミュニティwikiの回答

あなたも試すことができます:

_WHERE Age >= COALESCE(@MnimumAge, 0)
_

0は有効なAgeではないと仮定します。それ以外の場合は、デフォルトとして0ではなく-1を使用できます。

これにより、Ageをキーとするインデックスのシークが可能になります。


もう1つの主なオプションは、ステートメントにOPTION (RECOMPILE)を追加することです。

_CREATE PROCEDURE [dbo].[GetStudents]
    @MinimumAge int = NULL
AS
BEGIN
    SELECT S.* 
    FROM dbo.Students AS S
    WHERE @MinimumAge IS NULL
    OR S.Age >= @MinimumAge
    OPTION (RECOMPILE);
END;
_

これにより、ステートメント(プロシージャ全体ではなく)が再コンパイルされるときに、呼び出しごとにわずかなオーバーヘッドが追加されますが、パラメーター埋め込みの最適化が可能になるため、各実行で_@MinimumAge_の特定の値に対して最適な実行プランが得られます。詳細については、Paul Whiteによる パラメータスニッフィング、埋め込み、およびRECOMPILEオプション を参照してください。

また、Aaron Bertrandによる #BackToBasics:更新された「キッチンシンク」の例 を読んで、関連するQ&Aを確認することもできます SQL Server--ストアドプロシージャとプランキャッシュのロジック

4
user126897

私の経験から、最善の解決策は3番目の解決策で、「1つのタスクに1つの手順」というルールに従います。あなたの手順は現在、2つのタスクに対応しています-すべての従業員を返すことと、年齢に基づいてサブセットを返すこと。 1つの手順で2つの異なることは、将来の悲しみの源です。おそらく、あなたが与えたような単純化されたステートメントではありませんが、より複雑なコードでは可能性が高いです。

ここに私がそれをする方法があります:

CREATE PROCEDURE [dbo].[GetAllStudents]
AS
BEGIN
    SELECT  * 
    FROM    Students;
END;
GO

CREATE PROCEDURE [dbo].[GetOldStudentsByAge] 
@MinimumAge INT
AS
BEGIN
    SELECT * 
    FROM Students AS a 
    WHERE s.Age >= @MinimumAge;
END;
GO

CREATE PROCEDURE [dbo].[GetStudents]
@MinimumAge INT = NULL
AS
BEGIN
    IF @MinimumAge IS NULL 
        EXECUTE dbo.[GetAllStudents];
    ELSE 
        EXECUTE dbo.[GetOldStudentsByAge] @MinimumAge;
END;

これで、すべてのプロシージャには単一の目的があり、アプリケーションロジックが含まれていない単純なステートメントを使用します。IMHOの読み取りと保守がはるかに明確で簡単になり、異なるロジックを使用する後続の実行によるパラメータ/キャッシュの問題がなくなります。各サブプロシージャには、1つの適切なプランしかありません。 @MinimumAgeの値が大幅に異なり、別のプランを必要とする場合でも、パラメーターの問題が発生する可能性があります。それも解決できますが、別の問題です。

3
SQLRaptor

はい、列にインデックスが付けられている場合、クエリが遅くなる可能性があります。

SELECT * FROM Students  WHERE @MinimumAge IS NULL OR Age >= @MinimumAge --Index Scan

SELECT * FROM Students  WHERE Age > @MinimumAge --Index Seek

IS NULLはパフォーマンスに影響します。インデックスシークではなくインデックススキャンを取得できるため、追加のIOオーバーヘッドがあります。

追加のIOオーバーヘッドが無視できる場合、私はオプション1を使用してそれを監視します。オプション2を使用すると、SPそして、パラメータスニッフィングの問題に自分自身を開放します。これは、警告なしに突然あなたを襲い、オフィスで悲惨な日をもたらす可能性がある問題です。

2
pacreely

これはうまくいくと思います

select * from Students a where @MinimumAge is null  
union all
select * from Students a where s.Age >= @MinimumAge
1
paparazzo