스캔을 유발하는 지속 계산 열


9

일반 열을 지속 형 계산 열로 변환하면이 쿼리에서 인덱스 검색을 수행 할 수 없습니다. 왜?

2016 SP1 CU1을 포함한 여러 SQL Server 버전에서 테스트되었습니다.

재현

문제는 함께 table1, col7.

테이블과 쿼리는 원본의 일부 (및 단순화 된) 버전입니다. 쿼리를 다르게 다시 작성할 수 있다는 것을 알고 있으며 어떤 이유로 문제를 피할 수는 있지만 코드를 건드리지 table1않아야하며 왜 찾을 수 없는지에 대한 질문은 여전히 유효합니다.

Paul White가 보여 주듯이 (감사합니다!), 검색은 강제 실행되면 사용할 수 있으므로 질문은 : 옵티마이 저가 검색을 선택하지 않은 이유와 검색을 변경하지 않고 검색을 다르게 수행 할 수 있는지 여부는 다음과 같습니다. 암호?

문제가있는 부분을 명확히하기 위해 잘못된 실행 계획의 관련 스캔은 다음과 같습니다.

계획

답변:


12

탐색자가 검색을 선택하지 않은 이유


TL : DR 확장 된 계산 열 정의는 처음에 조인을 재정렬하는 옵티마이 저의 기능을 방해합니다. 시작점이 다르면 비용 기반 최적화는 옵티 마이저를 통해 다른 경로를 거쳐 다른 최종 계획을 선택하게됩니다.


세부

가장 단순한 쿼리를 제외한 모든 쿼리에 대해 옵티마이 저는 가능한 계획의 전체 공간과 같은 것을 탐색하지 않습니다. 대신 합리적인 계획의 출발점을 선택한 다음 합리적인 계획을 찾을 때까지 하나 이상의 검색 단계에서 논리적 및 물리적 변형을 탐색하는 데 많은 노력을 기울입니다.

두 경우에 대해 다른 계획 (최종 예상 비용이 다름)을 얻는 주된 이유는 시작점 이 서로 다르기 때문 입니다. 다른 장소에서 시작하여 최적화는 다른 장소에서 시작합니다 (탐색 및 구현 반복 횟수가 제한된 후에). 이것이 합리적으로 직관적이기를 바랍니다.

시작점 내가 언급은 다소 쿼리의 텍스트 표현을 기반으로하지만 변경이 정상화 바인딩, 구문 분석을 통과 내부 트리의 표현에, 및 쿼리 컴파일의 단순화 단계에있다.

중요하게, 정확한 시작점 은 옵티마이 저가 선택한 초기 결합 순서 에 따라 크게 다릅니다 . 이 선택은 통계가로드되기 전과 카디널리티 추정이 도출되기 전에 수행됩니다. 그러나 각 테이블의 총 카디널리티 (행 수)는 시스템 메타 데이터에서 얻은 것으로 알려져 있습니다.

따라서 초기 조인 순서는 휴리스틱을 기반으로 합니다 . 예를 들어, 옵티마이 저는 작은 테이블이 큰 테이블보다 먼저 결합되고 내부 조인이 외부 조인 (및 교차 조인)보다 먼저 오도록 트리를 다시 쓰려고합니다.

계산 열 이 있으면이 프로세스, 특히 외부 조인을 쿼리 트리로 푸시하는 옵티마이 저의 기능을 방해 합니다. 조인 재정렬이 발생하기 전에 계산 열이 기본 식으로 확장되므로 복잡한 식을지나 조인을 이동하는 것이 간단한 열 참조를 지나서 이동하는 것보다 훨씬 어렵 기 때문입니다.

관련된 트리는 상당히 크지 만 계산되지 않은 열 초기 쿼리 트리는 다음으로 시작합니다. (맨 위의 두 개의 외부 조인 참조)

LogOp_Select
    LogOp_Apply (x_jtLeftOuter) 
        LogOp_LeftOuterJoin
            LogOp_NAryJoin
                LogOp_LeftAntiSemiJoin
                    LogOp_NAryJoin
                        LogOp_Get TBL : dbo.table1 (별칭 : TBL : a4)
                        LogOp_Select
                            LogOp_Get TBL : dbo.table6 (별칭 : TBL : a3)
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL : [a3] .col18
                                ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
                        LogOp_Select
                            LogOp_Get TBL : dbo.table1 (별칭 : TBL : a1)
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL : [a1] .col2
                                ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
                        LogOp_Select
                            LogOp_Get TBL : dbo.table5 (별칭 : TBL : a2)
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL : [a2] .col2
                                ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL : [a4] .col2
                            ScaOp_Identifier QCOL : [a3] .col19
                    LogOp_Select
                        LogOp_Get TBL : dbo.table7 (별칭 : TBL : a7)
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL : [a7] .col22
                            ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL : [a4] .col2
                        ScaOp_Identifier QCOL : [a7] .col23
                LogOp_Select
                    LogOp_Get TBL : table1 (별칭 : TBL : cdc)
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL : [cdc] .col6
                        ScaOp_Const TI (smallint, ML = 2) XVAR (smallint, 소유하지 않음, 값 = 4)
                LogOp_Get TBL : dbo.table5 (별칭 : TBL : a5) 
                LogOp_Get TBL : table2 (별칭 : TBL : cdt)  
                ScaOp_Logical x_lopAnd
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL : [a5] .col2
                        ScaOp_Identifier QCOL : [cdc] .col2
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL : [a4] .col2
                        ScaOp_Identifier QCOL : [cdc] .col2
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL : [cdt] .col1
                        ScaOp_Identifier QCOL : [cdc] .col1
            LogOp_Get TBL : table3 (별칭 : TBL : ahcr)
            ScaOp_Comp x_cmpEq
                ScaOp_Identifier QCOL : [ahcr] .col9
                ScaOp_Identifier QCOL : [cdt] .col1

계산 된 열 쿼리 의 동일한 조각은 다음과 같습니다 .

LogOp_Select
    LogOp_Apply (x_jtLeftOuter)
        LogOp_NAryJoin
            LogOp_LeftAntiSemiJoin
                LogOp_NAryJoin
                    LogOp_Get TBL : dbo.table1 (별칭 : TBL : a4)
                    LogOp_Select
                        LogOp_Get TBL : dbo.table6 (별칭 : TBL : a3)
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL : [a3] .col18
                            ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
                    LogOp_Select
                        LogOp_Get TBL : dbo.table1 (별칭 : TBL : a1
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL : [a1] .col2
                            ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
                    LogOp_Select
                        LogOp_Get TBL : dbo.table5 (별칭 : TBL : a2)
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL : [a2] .col2
                            ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL : [a4] .col2
                        ScaOp_Identifier QCOL : [a3] .col19
                LogOp_Select
                    LogOp_Get TBL : dbo.table7 (별칭 : TBL : a7) 
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL : [a7] .col22
                        ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
                ScaOp_Comp x_cmpEq
                    ScaOp_Identifier QCOL : [a4] .col2
                    ScaOp_Identifier QCOL : [a7] .col23
            LogOp_Project
                LogOp_LeftOuterJoin
                    LogOp_Join
                        LogOp_Select
                            LogOp_Get TBL : table1 (별칭 : TBL : cdc) 
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL : [cdc] .col6
                                ScaOp_Const TI (smallint, ML = 2) XVAR (smallint, 소유하지 않음, 값 = 4)
                        LogOp_Get TBL : table2 (별칭 : TBL : cdt) 
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL : [cdc] .col1
                            ScaOp_Identifier QCOL : [cdt] .col1
                    LogOp_Get TBL : table3 (별칭 : TBL : ahcr) 
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL : [ahcr] .col9
                        ScaOp_Identifier QCOL : [cdt] .col1
                AncOp_PrjList 
                    AncOp_PrjEl QCOL : [cdc] .col7
                        ScaOp_Convert char collate 53256, Null, Trim, ML = 6
                            ScaOp_IIF varchar collate 53256, Null, Var, Trim, ML = 6
                                ScaOp_Comp x_cmpEq
                                    ScaOp_Intrinsic 이수
                                        ScaOp_ 본질적 권리
                                            ScaOp_Identifier QCOL : [cdc] .col4
                                            ScaOp_Const TI (int, ML = 4) XVAR (int, Not Owned, Value = 4)
                                    ScaOp_Const TI (int, ML = 4) XVAR (int, Not Owned, Value = 0)
                                ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 1) XVAR (varchar, 소유, 값 = Len, 데이터 = (0,))
                                ScaOp_Intrinsic 하위 문자열
                                    ScaOp_Const TI (int, ML = 4) XVAR (int, 소유하지 않음, 값 = 6)
                                    ScaOp_Const TI (int, ML = 4) XVAR (int, Not Owned, Value = 1)
                                    ScaOp_Identifier QCOL : [cdc] .col4
            LogOp_Get TBL : dbo.table5 (별칭 : TBL : a5)
            ScaOp_Logical x_lopAnd
                ScaOp_Comp x_cmpEq
                    ScaOp_Identifier QCOL : [a5] .col2
                    ScaOp_Identifier QCOL : [cdc] .col2
                ScaOp_Comp x_cmpEq
                    ScaOp_Identifier QCOL : [a4] .col2
                    ScaOp_Identifier QCOL : [cdc] .col2

통계가로드되고 초기 결합 순서가 설정된 직후 트리에서 초기 카디널리티 추정이 수행됩니다. 다른 순서로 조인을 갖는 것도 이러한 추정치에 영향을 미치므로 나중에 비용 기반 최적화 중에 영향을 미칩니다.

마지막으로이 섹션에서 트리의 중간에 외부 조인이 붙어 있으면 비용 기반 최적화 중에 일부 조인 재정렬 규칙이 일치하지 않을 수 있습니다.


계획 지침 (또는 등가 사용 USE PLAN힌트 - 당신의 쿼리에 대한 예를하는 것은 ) 검색 전략 변경 보다 목표 지향적 접근 방법, 안내 일반적인 형태로하고 제공된 템플릿의 기능. 이는 계획 지침 또는 힌트가 사용될 때 옵티마이 저가 계산 및 계산되지 않은 열 스키마 모두에 대해 동일한 탐색 계획을 찾을 있는 이유를 설명합니다 table1.

탐색을 수행하기 위해 다르게 행동 할 수 있는지 여부

이는 옵티마이 저가 자체적으로 수용 가능한 성능 특성을 가진 계획을 찾지 못한 경우에만 걱정해야 할 사항입니다.

모든 일반 튜닝 도구가 적용 가능할 수 있습니다. 예를 들어 쿼리를 더 간단한 부분으로 나누고, 사용 가능한 인덱싱을 검토 및 개선하고, 새로운 통계를 업데이트 또는 생성하는 등의 작업을 수행 할 수 있습니다.

이 모든 것들이 카디널리티 추정, 옵티 마이저를 통해 얻은 코드 경로 및 미묘한 방식으로 비용 기반 결정에 영향을 줄 수 있습니다.

궁극적으로 힌트 (또는 계획 지침)를 사용하는 것이 좋지만 일반적으로 이상적인 솔루션은 아닙니다.


의견의 추가 질문

쿼리 등을 단순화하는 것이 가장 좋지만 옵티마이 저가 최적화를 계속하고 동일한 결과를 얻는 방법 (추적 플래그)이 있습니까?

아니요, 철저한 검색을 수행하기위한 추적 플래그가 없으며 원하지 않습니다. 가능한 검색 공간은 넓고 우주의 나이를 초과하는 컴파일 시간은 잘 수신되지 않습니다. 또한 옵티마이 저는 가능한 모든 논리적 변환을 알지 못합니다 (아무도 아님).

또한 열이 지속될 때 복잡한 확장이 필요한 이유는 무엇입니까? 최적화 프로그램이 확장을 피하고 일반 열처럼 취급하며 동일한 시작점에 도달 할 수없는 이유는 무엇입니까?

계산 열은보기와 같이 확장되어 추가 최적화 기회를 제공합니다. 확장은 프로세스에서 나중에 예를 들어 지속 열 또는 인덱스와 다시 일치시킬 수 있지만 초기 결합 순서 가 수정 된 후에 발생합니다 .

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