계산 열 인덱스가 사용되지 않음


14

두 열이 같은지 여부를 기반으로 빠른 조회를 원합니다. 인덱스가있는 계산 열을 사용하려고했지만 SQL Server가이를 사용하지 않는 것 같습니다. 인덱스로 정적으로 채워진 비트 열을 사용하면 예상되는 인덱스 탐색을 얻습니다.

이와 같은 다른 질문이 있지만 인덱스를 사용하지 않는 이유에 초점을 맞춘 것은 없습니다.

테스트 테이블 :

CREATE TABLE dbo.Diffs
    (
    Id int NOT NULL IDENTITY (1, 1),
    DataA int NULL,
    DataB int NULL,
    DiffPersisted  AS isnull(convert(bit, case when [DataA] is null and [DataB] is not null then 1 when [DataA] <> [DataB] then 1 else 0 end), 0) PERSISTED ,
    DiffComp  AS isnull(convert(bit, case when [DataA] is null and [DataB] is not null then 1 when [DataA] <> [DataB] then 1 else 0 end), 0),
    DiffStatic bit not null,
    Primary Key (Id)
    )

create index ix_DiffPersisted on Diffs (DiffPersisted)
create index ix_DiffComp on Diffs (DiffComp)
create index ix_DiffStatic on Diffs (DiffStatic)

그리고 쿼리 :

select Id from Diffs where DiffPersisted = 1
select Id from Diffs where DiffComp = 1
select Id from Diffs where DiffStatic = 1

결과적인 실행 계획은 다음과 같습니다. 실행 계획

답변:


10

COALESCE대신 사용해보십시오 ISNULL. 를 사용 ISNULL하면 SQL Server는 더 좁은 인덱스에 대해 술어를 푸시 할 수 없으므로 정보를 찾기 위해 클러스터 된 클러스터를 스캔해야합니다.

CREATE TABLE dbo.Diffs
    (
    Id int NOT NULL IDENTITY (1, 1),
    DataA int NULL,
    DataB int NULL,
    DiffPersisted  AS COALESCE(convert(bit, case when [DataA] is null 
      and [DataB] is not null then 1 when [DataA] <> [DataB] 
      then 1 else 0 end), 0) PERSISTED ,
    DiffComp  AS COALESCE(convert(bit, case when [DataA] is null 
      and [DataB] is not null then 1 when [DataA] <> [DataB] 
      then 1 else 0 end), 0),
    DiffStatic bit not null,
    Primary Key (Id)
    );

즉, 정적 열을 고수하면 필터링 된 인덱스가 더 적합 할 수 있으며 I / O 비용이 낮습니다 (모두 일반적으로 필터 조건 자와 일치하는 행 수에 따라 다름).

CREATE INDEX ix_DiffStaticFiltered 
  ON dbo.Diffs(DiffStatic)
  WHERE DiffStatic = 1;

매우 흥미 롭습니다. COALESCE이 시점에서 그냥 제거 할 수있는 것처럼 보입니다 . 나는 생각 CASE문이 이미 반환 보장 된 01,하지만 ISNULLSQL 서버가 아닌 널 (NULL)을 얻을 것입니다 만, 그래서 존재 BIT계산 된 컬럼에 대해. 그러나 COALESCE여전히 널 입력 가능 열을 생성합니다. 따라서이 변경으로 인해을 (를) 포함하거나 포함하지 않고 COALESCE계산 열은 이제 널 입력 가능하지만 색인 검색을 사용할 수 있습니다.
Geoff Patterson

@Geoff 네, 맞습니다. 그러나 우리가 계산 열 정의 NULL로 알고 있기 때문에이 경우에는 가능한 출력이는 정말 아닙니다 정말 우리가 SELECT INTO의 소스로이 테이블을 사용하는 경우 문제.
Aaron Bertrand

이것은 놀라운 정보입니다-감사합니다! 최종 목표는 DataA 및 DataB 열이 레코드에서 비정규 화 된 열을 비동기식으로 업데이트 할 수 있도록 "더티"UUID로 사용되므로 Diff 플래그가 1 인 위치가 너무 많지 않아야한다는 것입니다. 필드에 두 개의 UUID를 모니터링하고 필드를 업데이트하는 트리거를 추가하려고 생각했습니다.
David Faivre

또한 @GeoffPatterson이 지적했듯이 COALESCE?를 사용할 수 없습니까? 왜 보관해야합니까?
David Faivre

@David 아마을 떨어 뜨릴 수 있습니다 COALESCE. 원래 코드의 모양과 의도를 유지하려고 시도했지만 코드가 없으면 테스트하지 않았으므로 테스트가 진행됩니다. (당신이 한 이유를 설명 할 수 없다 ISNULL처음에이 중 하나.)
아론 버트 랜드

5

이것은 가장 바깥 쪽 ISNULL을 사용할 때 SQL Server 계산 열 일치 논리의 특정 제한 사항이며 열의 데이터 형식은입니다 bit.

버그 리포트

이 문제를 피하기 위해 다음 해결 방법 중 하나를 사용할 수 있습니다.

  1. 가장 바깥 쪽을 사용하지 마십시오 ISNULL(계산 열을 만드는 유일한 방법 NOT NULL).
  2. bit데이터 유형을 계산 열의 최종 유형으로 사용하지 마십시오 .
  3. 계산 된 열을 PERSISTED만들고 추적 플래그 (174)를 활성화한다 .

세부

이 문제의 핵심은 추적 플래그 174가 없으면 쿼리에서 계산 된 모든 열 참조 (지속적 임)가 항상 쿼리 컴파일 초기에 기본 정의로 확장된다는 것입니다.

확장의 개념은 열 이름 만이 아니라 정의에서만 작동 할 수있는 단순화 및 재 작성을 가능하게한다는 것입니다. 예를 들어, 계산의 일부를 중복하거나 더 제한적으로 만들 수있는 계산 열을 참조하는 쿼리에 술어가있을 수 있습니다.

초기 단순화 및 다시 쓰기가 고려되면 쿼리 컴파일은 쿼리의 식을 계산 열 (쿼리 텍스트에서 원래 찾은 열뿐만 아니라 모든 계산 열)과 일치 시키려고합니다.

변경되지 않은 계산 열 식은 대부분의 경우 문제없이 원래 계산 열과 다시 일치합니다. bittype 의 표현식 과 가장 바깥 쪽 을 일치시킬 때 버그가있는 것으로 보입니다 ISNULL. 내부에 대한 자세한 검사에서 성공해야한다고 보여도이 특정 경우에는 일치하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.