免責事項:この回答の内容の一部は、DBAを尻込みさせる可能性があります。純粋なパフォーマンスの観点からアプローチしています。常にインデックススキャンを取得するときにインデックスシークを取得する方法です。
これで終わりです。
クエリは、「キッチンシンククエリ」と呼ばれるものです。単一のクエリで、さまざまな検索条件に対応できます。ユーザーが@status
値を設定した場合、そのステータスでフィルタリングする必要があります。場合@status
でNULL
、その上のすべてのステータスを返し、。
これにより、インデックス付けに問題が生じますが、検索条件はすべて「等しい」基準であるため、検索可能性とは関係ありません。
これはsargableです:
WHERE [status]=@status
SQL Server はインデックス内の単一の値を検索するのではなく、すべての行を評価する必要があるため、これは検索可能ではありませんISNULL([status], 0)
。
WHERE ISNULL([status], 0)=@status
台所の流しの問題をより簡単な形で再現しました:
CREATE TABLE #work (
A int NOT NULL,
B int NOT NULL
);
CREATE UNIQUE INDEX #work_ix1 ON #work (A, B);
INSERT INTO #work (A, B)
VALUES (1, 1), (2, 1),
(3, 1), (4, 1),
(5, 2), (6, 2),
(7, 2), (8, 3),
(9, 3), (10, 3);
以下を試すと、Aがインデックスの最初の列であっても、インデックススキャンが実行されます。
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE (@a IS NULL OR @a=A) AND
(@b IS NULL OR @b=B);
ただし、これによりインデックスシークが生成されます。
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE @a=A AND
@b IS NULL;
管理可能な量のパラメーター(この場合は2つ)を使用している限り、おそらくUNION
多数のシーククエリ(基本的には検索条件のすべての順列)を使用できます。3つの基準がある場合、これは乱雑に見え、4つでは完全に管理できなくなります。あなたは警告されました。
DECLARE @a int=4, @b int=NULL;
SELECT *
FROM #work
WHERE @a=A AND
@b IS NULL
UNION ALL
SELECT *
FROM #work
WHERE @a=A AND
@b=B
UNION ALL
SELECT *
FROM #work
WHERE @a IS NULL AND
@b=B
UNION ALL
SELECT *
FROM #work
WHERE @a IS NULL AND
@b IS NULL;
(B, A)
ただし、これら4つのうちの3つ目でインデックスシークを使用するには、2つ目のインデックスが必要になります。これらの変更でクエリがどのように見えるかを以下に示します(読みやすくするためのクエリのリファクタリングを含む)。
DECLARE @Status int = NULL,
@IsUserGotAnActiveDirectoryUser bit = NULL;
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE [Status]=@Status AND
@IsUserGotAnActiveDirectoryUser IS NULL
UNION ALL
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE [Status]=@Status AND
@IsUserGotAnActiveDirectoryUser=1 AND ActiveDirectoryUser<>''
UNION ALL
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE [Status]=@Status AND
@IsUserGotAnActiveDirectoryUser=0 AND (ActiveDirectoryUser IS NULL OR ActiveDirectoryUser='')
UNION ALL
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE @Status IS NULL AND
@IsUserGotAnActiveDirectoryUser IS NULL
UNION ALL
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE @Status IS NULL AND
@IsUserGotAnActiveDirectoryUser=1 AND ActiveDirectoryUser<>''
UNION ALL
SELECT [IdNumber], [Code], [Status], [Sex], [FirstName], [LastName],
[Profession], [BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE @Status IS NULL AND
@IsUserGotAnActiveDirectoryUser=0 AND (ActiveDirectoryUser IS NULL OR ActiveDirectoryUser='');
...さらにEmployee
、2つのインデックス列を逆にした追加のインデックスが必要になります。
完全をx=@x
期すために、私は暗黙的には、x
がと等しくなるNULL
ことNULL
はないので、それが不可能であることを意味することを言及する必要がありますNULL
。これにより、クエリが少し簡略化されます。
そして、はい、Aaron Bertrandの動的SQLの答えは、ほとんどの場合(つまり、再コンパイルを実行できる場合はいつでも)より良い選択です。
@Status
か?