NULL이 아닌 값 또는 NULL이 아닌 열을 확인하는 SQL 쿼리를 작성하는 가장 좋은 방법


17

기본값으로 NULL을 갖는 매개 변수가있는 SP가 있고 다음과 같은 쿼리를 수행하려고합니다.

SELECT ...
FROM ...
WHERE a.Blah = @Blah AND (a.VersionId = @VersionId OR (@VersionId IS NULL AND a.VersionId IS NULL));

WHERE비 NULL 값하는 NULL 값을 모두 상기 검사 @VersionId.

성능면에서 IF명령문 을 대신 사용하고 NULL이 아닌 것을 검색하는 쿼리와 NULL에 대한 쿼리를 복제하는 것이 더 좋 습니까? :

IF @VersionId IS NULL BEGIN
    SELECT ...
    FROM ...
    WHERE a.Blah = @Blah AND a.VersionId IS NULL;
ELSE BEGIN
    SELECT ...
    FROM ...
    WHERE a.Blah = @Blah AND a.VersionId = @VersionId;
END

아니면 쿼리 최적화 프로그램이 본질적으로 동일합니까?

최신 정보:

(참고 : SQL Server를 사용하고 있습니다)

(그리고 내가 아는 한, a.VersionId = @VersionId두 경우 모두 사용 하는 것이 효과가 없습니까?)



나는 일반적으로 다음을 사용한다 : ISNULL (a.VersionId, @VersionId) = @VersionId
628426

답변:


36

이 패턴

column = @argument OR (@argument IS NULL AND column IS NULL)

로 교체 가능

EXISTS (SELECT column INTERSECT SELECT @argument)

이를 통해 NULL을 NULL과 일치시키고 엔진이 column효율적으로 인덱스를 사용할 수 있습니다. 이 기술에 대한 훌륭한 심층 분석을 위해 Paul White의 블로그 기사를 참조하십시오.

특정 경우에 두 개의 인수가 있으므로 동일한 일치 기술을 함께 사용할 수 있습니다. 이렇게 @Blah하면 전체 WHERE 절을 다소 간결하게 다시 작성할 수 있습니다.

WHERE
  EXISTS (SELECT a.Blah, a.VersionId INTERSECT SELECT @Blah, @VersionId)

에 대한 색인으로 빠르게 작동합니다 (a.Blah, a.VersionId).


아니면 쿼리 최적화 프로그램이 본질적으로 동일합니까?

이 경우에 그렇습니다. SQL Server 2005 이후의 모든 버전 (최소한)에서 옵티마이 저는 패턴을 인식하고 col = @var OR (@var IS NULL AND col IS NULL)이를 적절한 IS비교로 바꿀 수 있습니다. 내부 재 작성 일치에 의존하기 때문에 이것이 항상 신뢰할 수없는 복잡한 경우가있을 수 있습니다.

2008 SP1 CU5 의 SQL Server 버전 에는 매개 변수 포함 최적화 를 사용하는 옵션도 있습니다. OPTION (RECOMPILE)여기서 매개 변수 또는 변수의 런타임 값은 컴파일 전에 리터럴로 쿼리에 포함됩니다.

따라서 적어도이 경우에는 선택의 여지가 스타일의 문제이지만 INTERSECT건축은 명백하고 콤팩트합니다.

다음 예제는 각 변형에 대한 '동일한'실행 계획을 보여줍니다 (리터럴 대 변수 참조는 제외됨).

DECLARE @T AS table
(
    c1 integer NULL,
    c2 integer NULL,
    c3 integer NULL

    UNIQUE CLUSTERED (c1, c2)
);

-- Some data
INSERT @T
    (c1, c2, c3)
SELECT 1, 1, 1 UNION ALL
SELECT 2, 2, 2 UNION ALL
SELECT NULL, NULL, NULL UNION ALL
SELECT 3, 3, 3;

-- Filtering conditions
DECLARE 
    @c1 integer,
    @c2 integer;

SELECT
    @c1 = NULL,
    @c2 = NULL;

-- Writing the NULL-handling out explicitly
SELECT * 
FROM @T AS T
WHERE 
(
    T.c1 = @c1
    OR (@c1 IS NULL AND T.c1 IS NULL)
)
AND 
(
    T.c2 = @c2
    OR (@c2 IS NULL AND T.c2 IS NULL)
);

-- Using INTERSECT
SELECT * 
FROM @T AS T
WHERE EXISTS 
(
    SELECT T.c1, T.c2 
    INTERSECT 
    SELECT @c1, @c2
);

-- Using separate queries
IF @c1 IS NULL AND @c2 IS NULL
    SELECT * 
    FROM @T AS T
    WHERE T.c1 IS NULL
    AND T.c2 IS NULL
ELSE IF @c1 IS NULL
    SELECT * 
    FROM @T AS T
    WHERE T.c1 IS NULL
    AND T.c2 = @c2
ELSE IF @c2 IS NULL
    SELECT * 
    FROM @T AS T
    WHERE T.c1 = @c1
    AND T.c2 IS NULL
ELSE
    SELECT * 
    FROM @T AS T
    WHERE T.c1 = @c1
    AND T.c2 = @c2;

-- Using OPTION (RECOMPILE)
-- Requires 2008 SP1 CU5 or later
SELECT * 
FROM @T AS T
WHERE 
(
    T.c1 = @c1
    OR (@c1 IS NULL AND T.c1 IS NULL)
)
AND 
(
    T.c2 = @c2
    OR (@c2 IS NULL AND T.c2 IS NULL)
)
OPTION (RECOMPILE);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.