임시 테이블이 탐색 및 책갈피 조회를 사용하는 동안 테이블 변수가 인덱스 스캔을 강제하는 이유는 무엇입니까?


18

테이블 변수를 사용하여 옵티마이 저가 인덱스 검색을 사용하지 못하고 북마크 조회와 인덱스 스캔을 사용하지 못하는 이유를 이해하려고합니다.

테이블 채우기 :

CREATE TABLE dbo.Test 
(
    RowKey INT NOT NULL PRIMARY KEY, 
    SecondColumn CHAR(1) NOT NULL DEFAULT 'x',
    ForeignKey INT NOT NULL 
) 

INSERT dbo.Test 
(
    RowKey, 
    ForeignKey
) 
SELECT TOP 1000000 
    ROW_NUMBER() OVER (ORDER BY (SELECT 0)),
    ABS(CHECKSUM(NEWID()) % 10)     
FROM sys.all_objects s1
CROSS JOIN sys.all_objects s2 

CREATE INDEX ix_Test_1 ON dbo.Test (ForeignKey) 

단일 레코드로 테이블 변수를 채우고 외래 키 열을 검색하여 기본 키와 두 번째 열을 찾아보십시오.

DECLARE @Keys TABLE (RowKey INT NOT NULL) 

INSERT @Keys (RowKey) VALUES (10)

SELECT 
    t.RowKey,
    t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    @Keys k
ON
    t.ForeignKey = k.RowKey

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

여기에 이미지 설명을 입력하십시오

이제 임시 테이블을 대신 사용하는 동일한 쿼리 :

CREATE TABLE #Keys (RowKey INT NOT NULL) 

INSERT #Keys (RowKey) VALUES (10) 

SELECT 
    t.RowKey,
    t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    #Keys k
ON
    t.ForeignKey = k.RowKey

이 쿼리 계획은 찾기 및 책갈피 조회를 사용합니다.

여기에 이미지 설명을 입력하십시오

옵티마이 저가 테이블 변수가 아닌 임시 테이블을 사용하여 책갈피 조회를 원하는 이유는 무엇입니까?

이 예제에서 테이블 변수는 스토어드 프로 시저에서 사용자 정의 테이블 유형을 통해 오는 데이터를 나타내는 데 사용됩니다.

외래 키 값이 수십만 번 발생하면 인덱스 탐색이 적절하지 않을 수 있음을 알고 있습니다. 이 경우 스캔이 더 나은 선택 일 수 있습니다. 내가 만든 시나리오의 경우 값이 10 인 행이 없었습니다. 여전히 동작이 흥미롭고 그 이유가 있는지 알고 싶습니다.

SQL 바이올린

추가해도 OPTION (RECOMPILE)동작이 변경되지 않았습니다. UDDT에는 기본 키가 있습니다.

@@VERSION Server 2008 R2 (SP2)-10.50.4042.0 (X64) (빌드 7601 : 서비스 팩 1) (하이퍼 바이저)

답변:


15

동작의 이유는 RowKey가 선행 열로 인덱스가 없기 때문에 SQL Server가 ForeignKey와 일치하는 행 수를 결정할 수 없기 때문입니다 (#temp 테이블의 통계에서이를 추론 할 수는 있지만 그렇지 않음). 테이블 변수 / UDTT에 대해 존재하므로) 예상 행을 약 100,000 행으로 설정하여 탐색 + 조회보다 스캔으로 처리하는 것이 좋습니다. SQL Server가 행이 하나만 있음을 인식 할 때는 너무 늦습니다.

UDTT를 다르게 구성 할 수 있습니다. 최신 버전의 SQL Server에서는 테이블 변수에 대한 보조 인덱스를 만들 수 있지만이 구문은 2008 R2에서 사용할 수 없습니다.

BTW 중첩 된 루프 조인을 암시하여 비트 맵 / 프로브를 피하려고하면 탐색 동작을 얻을 수 있습니다 (적어도 제한된 시험에서는).

DECLARE @Keys TABLE (RowKey INT PRIMARY KEY); -- can't hurt

INSERT @Keys (RowKey) VALUES (10);

SELECT 
     t.RowKey
    ,t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    @Keys k
ON
    t.ForeignKey = k.RowKey
    OPTION (LOOP JOIN);

나는 폴 화이트에서이 트릭을 배운 몇 년 전에. 물론 프로덕션 코드에 모든 종류의 조인 힌트를 넣는 데주의해야합니다. 사람들이 기본 개체를 변경하고 특정 유형의 조인이 더 이상 가능하지 않거나 더 이상 최적의 상태가 아닌 경우 실패 할 수 있습니다.

보다 복잡한 쿼리의 경우 SQL Server 2012 이상으로 이동하면 추적 플래그 2453 이 도움이 될 수 있습니다. 그러나이 플래그는이 간단한 조인에는 도움이되지 않았습니다. 그리고 동일한 면책 조항이 적용될 것입니다. 이것은 수많은 문서와 엄격한 회귀 테스트 절차없이 일반적으로 수행해서는 안되는 대안 일뿐입니다.

또한 서비스 팩 1은 오래 지원되지 않으므로 서비스 팩 3 + MS15-058을 받으십시오 .


3

테이블 변수와 임시 테이블은 여러 가지 방식으로 다르게 처리됩니다. 가 여기에 좋은 답변을 들은 다른 곳으로 세부의 많은이.

특히 귀하의 경우 임시 테이블에 추가 통계가 생성되고 병렬 계획이있을 수 있지만 테이블 변수에는 통계가 더 제한적이며 (열 수준 통계가 없음) 병렬 계획이 없다는 사실이 귀하의 주범이라고 생각합니다.

저장 프로 시저 기간 동안 테이블 변수를 임시 테이블로 덤프하는 것이 좋습니다.

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