SQL Server 2016에서 SUBSTRING ()이 포함 된 조건 자에 대한 추정치가 변경 되었습니까?


13

SUBSTRING () 또는 다른 문자열 함수를 포함하는 술어의 카디널리티 추정 방법에 대한 SQL Server 2016의 변경 사항에 대한 문서 나 연구가 있습니까?

내가 묻는 이유는 호환성 모드 130에서 성능이 저하 된 쿼리를보고 있었고 그 이유는 SUBSTRING ()에 대한 호출이 포함 된 WHERE 절과 일치하는 행 수의 추정치 변경과 관련이 있기 때문입니다. 쿼리 다시 쓰기 문제를 해결했지만 SQL Server 2016에서이 영역의 변경 사항에 대한 설명서를 알고있는 사람이 있는지 궁금합니다.

데모 코드는 다음과 같습니다. 이 테스트 사례에서는 추정치가 매우 비슷하지만 정확도는 데이터에 따라 다릅니다.

테스트 사례에서, compat 레벨 120에서 SQL Server는 추정에 히스토그램을 사용하는 것으로 보이지만 compat 레벨 130에서 SQL Server는 테이블 일치의 고정 된 10 %를 가정하는 것으로 보입니다.

CREATE DATABASE MyStringTestDB;
GO
USE MyStringTestDB;
GO
DROP TABLE IF EXISTS dbo.StringTest;
CREATE TABLE dbo.StringTest ( [TheString] varchar(15) );
GO
INSERT INTO dbo.StringTest
VALUES
( 'Y5_CLV' );
INSERT INTO dbo.StringTest
VALUES
( 'Y5_EG3' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_NE' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_PQT' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_T2V' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_TT4' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_ZKK' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_LW6' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_QO3' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_TZ7' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_UZZ' );

CREATE CLUSTERED INDEX IX_Clustered ON dbo.StringTest (TheString);

/* 
Uses fixed % for estimate; 1.1 rows estimated in this case.
    Plan for computation:
        CSelCalcFixedFilter (0.1) <----
            Selectivity: 0.1
*/
ALTER DATABASE MyStringTestDB SET compatibility_level = 130;
GO
SELECT * 
FROM dbo.StringTest 
WHERE SUBSTRING(TheString, 1, CHARINDEX('_',TheString) - 1) = 'ZZ'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);

/* 
Uses histogram to get estimate of 1
 CSelCalcPointPredsFreqBased <----
      Distinct value calculation:
          CDVCPlanLeaf
              0 Multi-Column Stats, 1 Single-Column Stats, 0 Guesses
      Individual selectivity calculations:
          (none)
    Loaded histogram for column QCOL: [DBA].[dbo].[StringTest].TheString from stats with id 1
*/
ALTER DATABASE MyStringTestDB SET compatibility_level = 120;
GO
SELECT * 
FROM dbo.StringTest 
WHERE SUBSTRING(TheString, 1, CHARINDEX('_',TheString) - 1) = 'ZZ'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);

/*
-- Simpler rewrite; works fine in both compat levels and gets better estimate.
SELECT * 
FROM dbo.StringTest 
WHERE TheString LIKE 'ZZ[_]%'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);
*/

1
특정 질문에 대해서는 잘 모르지만 Y5_EG3문자열이 코드이고 항상 대문자이면 이진 데이터 정렬을 항상 시도하면 Latin1_General_100_BIN2필터링 작업 속도가 향상됩니다. 그냥 추가 COLLATE Latin1_General_100_BIN2받는 사람 CREATE TABLE바로 후, 문 varchar(15). 계획 생성 / 추정에 영향을 미치는지 궁금합니다.
Solomon Rutzky

답변:


8

나는 어떤 문서도 모른다. 나는 이것을 조사하고 의견을 말하기에는 너무 긴 관측을했다.

10 % 추정치가 항상 저하되는 것은 아닙니다. 다음 예제를 보자.

TRUNCATE TABLE dbo.StringTest

INSERT INTO dbo.StringTest
SELECT TOP (1000000) 'ZZ_' + LEFT(NEWID(), 12)
FROM   master..spt_values v1,
       master..spt_values v2;

WHERE귀하의 질문에 있는 조항.

WHERE SUBSTRING(TheString, 1, CHARINDEX('_',TheString) - 1) = 'ZZ'

테이블에는 백만 개의 행이 있습니다. 모두 술어와 일치합니다. compat level 130에서 10 % 추측은 100,000의 추정치를 산출합니다. 120 미만에서는 예상 행이 1.03913입니다.

120 비헤이비어는 히스토그램을 사용하지만 별개의 행 수만 가져옵니다. 필자의 경우 밀도 벡터는 1.039131E-06을 표시하며 예상 행 수를 얻기 위해 테이블 ​​카디널리티를 곱합니다. 모든 값은 실제로 다르지만 모두 술어와 일치합니다.

query_optimizer_estimate_cardinality확장 이벤트를 추적하면 130 아래에 두 개의 다른 <StatsCollection Name="CStCollFilter"이벤트가 있음을 알 수 있습니다. 첫 번째는 100,000을 추정합니다. 두 번째 것은 히스토그램을로드하고 CSelCalcPointPredsFreqBased / DistinctCountCalculator를 사용하여 1.04 추정치를 얻습니다. 이 두 번째 결과는 사용되지 않은 것으로 나타납니다.

관찰 한 동작은 130에서 일관되게 적용되지 않습니다 ORDER BY TheString. 120이 한 행에 대한 메모리 부여로 어려움을 겪음에 따라 130 추정기의 확실한 승리가 될 것으로 기대하지만이 작은 변경으로 인해 예상 행이 줄어 들었습니다. 130의 경우도 1.03913입니다.

추가 OPTION (QUERYRULEOFF SelectToFilter)하면 정렬로 들어가는 추정값이 100,000으로 되돌아 가지만 메모리 부여는 증가하지 않으며 정렬로 나오는 추정값은 여전히 ​​테이블 고유 값을 기반으로합니다.

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

이와 유사하게 쿼리가 병렬 계획을 얻도록 병렬 처리에 대한 비용 임계 값을 조정하면 130 개의 경우에 더 낮은 추정값으로 되돌릴 수 있습니다. 추가 QUERYTRACEON 8757하면 추정치가 낮아집니다. 10 % 추정치는 사소한 계획에 대해서만 유지되는 것으로 보입니다.

제안한 다시 쓰기

WHERE TheString LIKE 'ZZ[_]%'

두 가지 모두에 대해 훨씬 우수한 추정치를 보여줍니다. 이것에 대한 출력은

  CSelCalcTrieBased

      Column: QCOL: [MyStringTestDB].[dbo].[StringTest].TheString

시도한 것을 보여줍니다 . 이에 대한 자세한 정보는 바로 위의 문자열 요약 통계 섹션에 있습니다 .

그러나 원래 쿼리와 동일하지 않습니다. 첫 번째 인스턴스 _는 이제 동적으로 발견되지 않고 항상 세 번째 문자 인 것으로 가정됩니다.

이 가정이 원래 쿼리에 하드 코딩 된 경우

 WHERE SUBSTRING(TheString, 1, 3) = 'ZZ_'

추정 방법이 변경되고 CSelCalcHistogramComparison(INTERVAL)추정 된 행이 정확 해집니다.

그것을 범위로 변환 할 수 있습니다

WHERE TheString >=  'ZZ_' AND TheString < ???

히스토그램을 사용하여 해당 범위의 값을 가진 행 수를 추정하십시오.

그러나 이것은 카디널리티 추정에만 적용됩니다. LIKE런타임시 범위 탐색을 사용할 수 있으므로 선호됩니다. SUBSTRING(TheString, 1, 3)또는 LEFT(TheString, 3)할 수 없습니다.

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