면책 조항 :이 답변의 일부 내용은 DBA를 약화시킬 수 있습니다. 순수한 성능 관점에서 접근하고 있습니다. 항상 인덱스 스캔을받을 때 인덱스를 찾는 방법.
그 길을 벗어나면 여기로갑니다.
검색어는 "주방 싱크 쿼리"라고하며, 가능한 다양한 검색 조건을 충족시키는 단일 쿼리입니다. 사용자 @status
가 값을 설정 하면 해당 상태를 필터링하려고합니다. 경우 @status
이다 NULL
, 등등의 모든 상태를 반환합니다.
이로 인해 색인 생성에 문제가 발생하지만 모든 검색 조건이 "같음"기준이기 때문에 Sargability와는 관련이 없습니다.
이것은 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
기본적으로 검색 기준의 모든 순열과 같은 많은 검색 쿼리 일 수 있습니다. 세 가지 기준이 있다면 복잡해 보일 수 있으며, 네 가지 기준은 완전히 관리 할 수 없습니다. 경고를 받았습니다.
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;
이 네 가지 중 세 번째가 Index Seek를 사용하려면에 대한 두 번째 인덱스가 필요합니다 (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
있습니까?