SQL Server 2014에서 LEN () 함수가 카디널리티를 과소 평가하는 이유는 무엇입니까?


26

문자열 열이있는 테이블과 특정 길이의 행을 검사하는 술어가 있습니다. SQL Server 2014에서 확인하는 길이에 관계없이 예상 1 행이 표시됩니다. 실제로 수천 또는 수백만 개의 행이 있고 SQL Server가이 테이블을 중첩 루프의 외부에 배치하도록 선택하기 때문에 계획이 매우 좋지 않습니다.

SQL Server 2014의 카디널리티 예상 1.0003에 대한 설명이있는 반면 SQL Server 2012는 31,622 개의 행을 추정합니까? 좋은 해결 방법이 있습니까?

다음은이 문제를 간단히 재현 한 것입니다.

-- Create a table with 1MM rows of dummy data
CREATE TABLE #customers (cust_nbr VARCHAR(10) NOT NULL)
GO

INSERT INTO #customers WITH (TABLOCK) (cust_nbr)
    SELECT TOP 1000000 
        CONVERT(VARCHAR(10),
        ROW_NUMBER() OVER (ORDER BY (SELECT NULL))) AS cust_nbr
    FROM master..spt_values v1
    CROSS JOIN master..spt_values v2
GO

-- Looking for string of a certain length.
-- While both CEs yield fairly poor estimates, the 2012 CE is much
-- more conservative (higher estimate) and therefore much more likely
-- to yield an okay plan rather than a drastically understimated loop join.
-- 2012: 31,622 rows estimated, 900K rows actual
-- 2014: 1 row estimated, 900K rows actual
SELECT COUNT(*)
FROM #customers
WHERE LEN(cust_nbr) = 6
OPTION (QUERYTRACEON 9481) -- Optionally, use 2012 CE
GO

다음은 추가 테스트를 보여주는보다 완전한 스크립트입니다.

또한 SQL Server 2014 Cardinality Estimator에 대한 백서를 읽었 지만 상황을 명확히하는 내용을 찾지 못했습니다.

답변:


20

레거시 CE의 경우 행의 3.16228 %에 대한 추정치이며 열 = 리터럴 술어에 사용되는 "매직 숫자"휴리스틱입니다 (조건 자 구성을 기반으로하는 다른 휴리스틱 LEN이 있음). 기존 CE 결과는이 추측 프레임 워크와 일치합니다). Joe Sack 의 통계없을 때의 Selectivity Guesses 및 Ian Jose의 Constant-Constant Comparison Estimation 에 대한 게시물에서 이에 대한 예를 볼 수 있습니다 .

-- Legacy CE: 31622.8 rows
SELECT  COUNT(*)
FROM    #customers
WHERE   LEN(cust_nbr) = 6
OPTION  ( QUERYTRACEON 9481); -- Legacy CE
GO

이제 새로운 CE 동작에 대해서는 이제 옵티마이 저가 볼 수 있습니다 (통계를 사용할 수 있음). 아래의 계산기 출력을 살펴보면서 통계의 관련 자동 생성을 포인터로 볼 수 있습니다.

-- New CE: 1.00007 rows
SELECT  COUNT(*)
FROM    #customers
WHERE   LEN(cust_nbr) = 6
OPTION  ( QUERYTRACEON 2312 ); -- New CE
GO

-- View New CE behavior with 2363 (for supported option use XEvents)
SELECT  COUNT(*)
FROM    #customers
WHERE   LEN(cust_nbr) = 6
OPTION  (QUERYTRACEON 2312, QUERYTRACEON 2363, QUERYTRACEON 3604, RECOMPILE); -- New CE
GO

/*
Loaded histogram for column QCOL:
[tempdb].[dbo].[#customers].cust_nbr from stats with id 2
Using ambient cardinality 1e+006 to combine distinct counts:
  999927

Combined distinct count: 999927
Selectivity: 1.00007e-006
Stats collection generated:
  CStCollFilter(ID=2, CARD=1.00007)
      CStCollBaseTable(ID=1, CARD=1e+006 TBL: #customers)

End selectivity computation
*/

EXEC tempdb..sp_helpstats '#customers';


--Check out AVG_RANGE_ROWS values (for example - plenty of ~ 1)
DBCC SHOW_STATISTICS('tempdb..#customers', '_WA_Sys_00000001_B0368087');
--That's my Stats name yours is subject to change

불행하게도 논리는 고유 한 값의 수에 대한 추정에 의존하며, 이는 LEN함수 의 효과에 맞게 조정되지 않습니다 .

가능한 해결 방법

를 다음 LEN과 같이 다시 쓰면 두 CE 모델 모두에서 trie 기반 추정치를 얻을 수 있습니다 LIKE.

SELECT COUNT_BIG(*)
FROM #customers AS C
WHERE C.cust_nbr LIKE REPLICATE('_', 6);

LIKE 플랜


사용 된 추적 플래그에 대한 정보 :

  • 2363 :로드되는 통계를 포함하여 많은 정보를 보여줍니다.
  • 3604 : DBCC 명령의 출력을 메시지 탭에 인쇄합니다.

13

SQL 2014의 카디널리티 추정치 1.0003에 대한 설명이있는 반면 SQL 2012는 31,622 개의 행을 추정합니까?

@ Zane의 대답 이이 부분을 잘 다루고 있다고 생각 합니다.

좋은 해결 방법이 있습니까?

해당 계산 열에 대해 비 지속 계산 열을 LEN(cust_nbr)생성하고 선택적으로 비 클러스터형 인덱스를 생성 할 수 있습니다. 정확한 통계를 얻을 수 있습니다.

나는 약간의 테스트를했고 여기 내가 찾은 것이있다 :

  • 인덱스가 정의되지 않은 경우 비 지속 계산 열에서 통계가 자동으로 작성되었습니다.
  • 계산 열에 비 클러스터형 인덱스를 추가하면 도움이되지 않았을뿐 아니라 실제로 성능이 약간 저하되었습니다. 약간 높은 CPU 및 경과 시간. 예상 비용이 약간 더 높음 (가치가있는 금액)
  • 계산 열을 PERSISTED(인덱스 없음) 으로 만드는 것이 다른 두 변형보다 낫습니다. 예상 행이 더 정확했습니다. CPU 및 경과 시간은 더 좋았습니다 (행당 아무것도 계산할 필요가 없기 때문에 예상 한대로).
  • 나는 경우에도, (그것 때문에이 계산되기) 계산 된 열에서 필터링 된 인덱스 또는 필터링 된 통계를 만들 수 없습니다 PERSISTED:-(

1
지속 여부를 철저히 비교해 주셔서 감사합니다. 지속 형 계산 열에 장점이 있더라도 비지 속형은 식에 대한 통계가 유익한 경우 약간의 오버 헤드로 매우 빠른 승리를 거둘 수 있습니다.
제프 패터슨
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.