SQL Server가 통계와 일치하지 않는 것으로 쉽게 입증 될 수있는 추정치를 제시하는 이유를 이해하는 데 어려움을 겪고 있습니다.
일관성
일관성에 대한 일반적인 보장은 없습니다. 추정치는 상이한 통계적 방법을 사용하여 상이한 시간에 상이한 (그러나 논리적으로 동등한) 서브 트리에서 상이한 시간에 계산 될 수있다.
이 두 개의 동일한 서브 트리를 결합하면 교차 곱을 생성해야한다고하는 논리에는 아무런 문제가 없지만 추론의 선택이 다른 것보다 더 건전하다고 말할 것도 없습니다.
초기 추정
특정한 경우, 조인에 대한 초기 카디널리티 추정은 두 개의 동일한 서브 트리에서 수행되지 않습니다 . 당시의 나무 모양은 다음과 같습니다.
LogOp_Join
LogOp_GbAgg
LogOp_LeftOuterJoin
LogOp_Get TBL : ar
LogOp_Select
LogOp_Get TBL : tcr
ScaOp_Comp x_cmpEq
ScaOp_Identifier [tcr] .rId
ScaOp_Const 값 = 508
ScaOp_Logical x_lopAnd
ScaOp_Comp x_cmpEq
ScaOp_Identifier [ar] .fId
ScaOp_Identifier [tcr] .fId
ScaOp_Comp x_cmpEq
ScaOp_Identifier [ar] .bId
ScaOp_Identifier [tcr] .bId
AncOp_PrjList
AncOp_PrjEl Expr1003
ScaOp_AggFunc stopMax
ScaOp_Convert int
ScaOp_Identifier [tcr] .isS
LogOp_Select
LogOp_GbAgg
LogOp_LeftOuterJoin
LogOp_Get TBL : ar
LogOp_Select
LogOp_Get TBL : tcr
ScaOp_Comp x_cmpEq
ScaOp_Identifier [tcr] .rId
ScaOp_Const 값 = 508
ScaOp_Logical x_lopAnd
ScaOp_Comp x_cmpEq
ScaOp_Identifier [ar] .fId
ScaOp_Identifier [tcr] .fId
ScaOp_Comp x_cmpEq
ScaOp_Identifier [ar] .bId
ScaOp_Identifier [tcr] .bId
AncOp_PrjList
AncOp_PrjEl Expr1006
ScaOp_AggFunc stopMin
ScaOp_Convert int
ScaOp_Identifier [ar] .isT
AncOp_PrjEl Expr1007
ScaOp_AggFunc stopMax
ScaOp_Convert int
ScaOp_Identifier [tcr] .isS
ScaOp_Comp x_cmpEq
ScaOp_Identifier Expr1006
ScaOp_Const 값 = 1
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL : [ar] .fId
ScaOp_Identifier QCOL : [ar] .fId
제 조인 입력은, 투영 집합체 얻어 간략화 갖고, 상기 제 2 입력은 술어 가지고 가입 t.isT = 1
하는 경우, 그 아래를 가압 t.isT
이다 MIN(CONVERT(INT, ar.isT))
. 그럼에도 불구하고 isT
술어에 대한 선택성 계산 CSelCalcColumnInInterval
은 히스토그램 에서 사용할 수 있습니다 .
CSelCalcColumnInInterval
칼럼 : COL : Expr1006
열 QCOL에 대한로드 된 히스토그램 : ID가 3 인 통계에서 [ar] .isT
선택성 : 4.85248e-005
통계 수집 생성 :
CStCollFilter (ID = 11, CARD = 1)
CStCollGroupBy (ID = 10, 카드 = 20608)
CStCollOuterJoin (ID = 9, 카드 = 20608 x_jtLeftOuter)
CStCollBaseTable (ID = 3, 카드 = 20608 TBL : ar)
CStCollFilter (ID = 8, 카드 = 1)
CStCollBaseTable (ID = 4, 카드 = 28 TBL : tcr)
(정확한) 기대는이 술어에 의해 20,608 행이 1 행으로 줄어드는 것입니다.
조인 추정
이제 다른 조인 입력에서 20,608 개의 행이이 한 행과 어떻게 일치하는지 문제가됩니다.
LogOp_Join
CStCollGroupBy (ID = 7, 카드 = 20608)
CStCollOuterJoin (ID = 6, 카드 = 20608 x_jtLeftOuter)
...
CStCollFilter (ID = 11, CARD = 1)
CStCollGroupBy (ID = 10, 카드 = 20608)
...
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL : [ar] .fId
ScaOp_Identifier QCOL : [ar] .fId
일반적으로 조인을 추정하는 방법에는 여러 가지가 있습니다. 예를 들면 다음과 같습니다.
- 각 하위 트리의 각 계획 연산자에서 새 히스토그램을 도출하고 조인에 정렬하고 (필요에 따라 보간 단계 값 보간) 이들이 어떻게 일치하는지 확인합니다. 또는
- 히스토그램의 간단한 '거친'정렬을 수행하십시오 (단계별이 아닌 최소값과 최대 값 사용). 또는
- 기본 테이블에서 필터링없이 조인 열에 대해서만 별도의 선택성을 계산 한 다음 비조 인 술어의 선택성 효과를 추가하십시오.
- ...
사용중인 카디널리티 추정기 및 일부 휴리스틱에 따라 이들 중 하나 (또는 변형)를 사용할 수 있습니다. 자세한 내용은 SQL Server 2014 카디널리티 추정기 로 쿼리 계획 최적화를 참조하십시오 .
곤충?
이제 질문에서 언급 했듯이이 경우 '단순'단일 열 조인 (on fId
)은 CSelCalcExpressionComparedToExpression
계산기를 사용합니다 .
계산 계획 :
CSelCalcExpressionComparedToExpression [ar] .fId x_cmpEq [ar] .fId
열 QCOL에 대한로드 된 히스토그램 : [ar] .bID가 2 인 통계의 ID
열 QCOL에 대한로드 된 히스토그램 : ID가 1 인 통계의 [ar] .fId
선택도 : 0
이 계산은 필터링 된 1 개의 행으로 20,608 개의 행을 결합하면 선택성이 0이됩니다. 일치하는 행이 없습니다 (최종 계획에서 한 행으로보고 됨). 이것이 잘못 되었습니까? 네, 아마도 새 CE에 버그가있을 것입니다. 하나의 행이 모든 행과 일치하거나 전혀 일치하지 않는다고 주장 할 수 있으므로 결과는 합리적 일 수 있지만 그렇지 않은 이유가 있습니다.
세부 사항은 실제로 까다로울 수 있지만 fId
, 필터의 선택도에 의해 수정되고 20608 * 20608 * 4.85248e-005 = 20608
행을 제공하는 필터링 되지 않은 히스토그램을 기반으로 한 추정치 는 매우 합리적입니다.
이 계산을 따르는 것은 CSelCalcSimpleJoinWithDistinctCounts
대신 계산기 를 사용하는 것을 의미합니다 CSelCalcExpressionComparedToExpression
. 이를 수행하는 문서화 된 방법은 없지만 궁금한 점이 있으면 문서화되지 않은 추적 플래그 9479를 활성화 할 수 있습니다.
최종 조인은 두 개의 단일 행 입력에서 20,608 개의 행을 생성하지만 놀랄 일이 아닙니다. TF 9481에 따라 최초 CE에서 생산 한 것과 동일한 계획입니다.
나는 세부 사항이 까다 롭고 조사하는 데 시간이 많이 걸리는 것을 언급했지만 내가 알 수있는 한 문제의 근본 원인은 rId = 508
선택성 이없는 술어와 관련이 있습니다. 이 제로 추정값은 일반적인 방식으로 한 행으로 올라가므로 입력 트리에서 더 낮은 술어를 설명 할 때 해당 조인에서 제로 선택성 추정값에 기여하는 것으로 보입니다 (따라서에 대한로드 통계 bId
).
외부 조인이 하나의 행으로 올리지 않고 0 행 내부 측 추정값을 유지하도록 허용하면 (모든 외부 행이 규정 됨) 두 계산기를 사용하여 '버그가없는'조인 추정치가 제공됩니다. 이것을 탐구하고 싶다면, 문서화되지 않은 추적 플래그는 9473 (단독)입니다 :
결합 카디널리티 추정의 동작은 CSelCalcExpressionComparedToExpression
문서화되지 않은 다른 변형 플래그로``bId ''를 설명하지 않도록 수정할 수도 있습니다 (9494). 나는 당신이 그런 것들에 관심이 있다는 것을 알고 있기 때문에이 모든 것을 언급합니다. 그들이 해결책을 제공하기 때문이 아닙니다. 문제를 Microsoft에보고하고 문제를 해결하기 전까지는 쿼리를 다르게 표현하는 것이 가장 좋은 방법 일 것입니다. 의도적 인 행동인지 아닌지에 관계없이 회귀에 대해 듣는 것에 관심을 가져야합니다.
마지막으로, 재생산 스크립트에서 언급 된 다른 것을 정리하기 위해 : 질문 계획에서 필터의 최종 위치 GbAggAfterJoinSel
는 결합 출력이 작은 값을 가지므로 결합과 필터를 결합 위로 이동시키는 비용 기반 탐색의 결과입니다. 행 수 예상대로 필터가 처음에 결합 아래에있었습니다.