オプション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つだけ追加するためにすべてのコードを複製したくありません。
あなたも試すことができます:
_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--ストアドプロシージャとプランキャッシュのロジック
私の経験から、最善の解決策は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の値が大幅に異なり、別のプランを必要とする場合でも、パラメーターの問題が発生する可能性があります。それも解決できますが、別の問題です。
はい、列にインデックスが付けられている場合、クエリが遅くなる可能性があります。
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そして、パラメータスニッフィングの問題に自分自身を開放します。これは、警告なしに突然あなたを襲い、オフィスで悲惨な日をもたらす可能性がある問題です。
これはうまくいくと思います
select * from Students a where @MinimumAge is null
union all
select * from Students a where s.Age >= @MinimumAge