免责声明:此答案中的某些内容可能会使DBA退缩。我是从纯粹的性能角度来解决问题的-当您始终获得索引扫描时如何获得索引查找。
有了这些,就可以了。
您的查询就是所谓的“厨房水槽查询”-单个查询旨在满足一系列可能的搜索条件。如果用户设置@status为一个值,则要过滤该状态。如果@status为NULL,则返回所有状态,依此类推。
这会引入索引问题,但它们与可持久性无关,因为所有搜索条件都“等于”条件。
这是可确定的:
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;
只要您使用数量可管理的参数(在您的情况下为两个),您就可能只是UNION一堆搜索查询-基本上是搜索条件的所有排列。如果您有三个条件,那么看起来会很混乱,而四个条件则完全无法管理。您已被警告。
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)。这是这些更改后查询的外观(包括我对查询的重构,使其更具可读性)。
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在两个索引列颠倒的情况下打开附加索引。
为了完整起见,我应该提到,x=@x隐含表示x不能为,NULL因为NULL它从不等于NULL。这样可以简化查询。
而且,是的,在大多数情况下(即只要您可以使用重新编译器),Aaron Bertrand的动态SQL答案是一个更好的选择。
@Status?