Como o otimizador do SQL Server estima o número de linhas em uma tabela associada?

13

Estou executando esta consulta no banco de dados AdventureWorks2012 :

SELECT 
    s.SalesOrderID,
    d.CarrierTrackingNumber,
    d.ProductID,
    d.OrderQty
FROM Sales.SalesOrderHeader s 
JOIN Sales.SalesOrderDetail d 
    ON s.SalesOrderID = d.SalesOrderID
WHERE s.CustomerID = 11077

Se eu olhar o plano de execução estimado, vejo o seguinte:

insira a descrição da imagem aqui

A busca inicial do índice (canto superior direito) está usando o índice IX_SalesOrderHeader_CustomerID e pesquisando no literal 11077. Ele tem uma estimativa de 2.6192 linhas.

insira a descrição da imagem aqui

Se eu usar DBCC SHOW_STATISTICS ('Sales.SalesOrderHeader', 'IX_SalesOrderHeader_CustomerID') WITH HISTOGRAM, mostra que o valor 11077 está entre as duas chaves amostradas 11019 e 11091.

insira a descrição da imagem aqui

O número médio de linhas distintas entre 11019 e 11091 é 2.619718 ou arredondado para 2.61972, que é o valor das linhas estimadas mostradas para a busca do índice.

A parte que eu não entendo é o número estimado de linhas para a busca de índice em cluster na tabela SalesOrderDetail.

insira a descrição da imagem aqui

Se eu correr DBCC SHOW_STATISTICS ('Sales.SalesOrderDetail', 'PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID'):

insira a descrição da imagem aqui

Portanto, a densidade do SalesOrderID (no qual estou ingressando) é 3.178134E-05. Isso significa que 1 / 3.178134E-05 (31465) é igual ao número de valores únicos de SalesOrderID na tabela SalesOrderDetail.

Se houver 31465 SalesOrderID exclusivos no SalesOrderDetail, com uma distribuição uniforme, o número médio de linhas por SalesOrderID será 121317 (número total de linhas) dividido por 31465. A média é 3,85561

Portanto, se o número estimado de linhas a serem repetidas for 2.61972 e a média a ser retornada em 3.85561, eu pensaria que o número estimado de linhas seria 2.61972 * 3.85561 = 10.10062.

Mas o número estimado de linhas é 11.4867.

Eu acho que meu entendimento da segunda estimativa está incorreto e os números diferentes parecem indicar isso. o que estou perdendo?

8kb
fonte

Respostas:

20

Eu acho que meu entendimento da segunda estimativa está incorreto e os números diferentes parecem indicar isso. o que estou perdendo?

Usando o estimador de cardinalidade do SQL Server 2012, a seletividade da associação impulsiona o número estimado de linhas na parte interna dos loops aninhados e não o contrário.

O número 11.4867 é derivado (para exibição no plano de show) dividindo a cardinalidade estimada calculada da saída de junção (30.0919) pelo número de iterações (2.61972). O resultado, usando aritmética de ponto flutuante de precisão única, é 11,4867 .

É realmente tão simples assim. Observe que a seletividade de junção (lógica) é independente da escolha do operador de junção física. Permanece o mesmo se a junção for finalmente executada usando um operador físico Nested Loops, Hash ou Merge Join.

No SQL Server 2012 e versões anteriores, a seletividade de junção (como um todo) é estimada usando os SalesOrderIDhistogramas de cada tabela (calculados para cada etapa do histograma, após o alinhamento do limite da etapa usando interpolação linear conforme necessário). O SalesOrderIDhistograma associado à SalesOrderHeadertabela também é ajustado para o efeito de escala do CustomerIDfiltro independente .

Isso não quer dizer que exista algo fundamentalmente "errado" no cálculo alternativo proposto na pergunta; apenas faz um conjunto diferente de suposições. Sempre haverá maneiras diferentes de calcular ou combinar estimativas para uma determinada sequência de operações lógicas. Não há garantia geral de que métodos estatísticos diferentes aplicados aos mesmos dados produzam as mesmas respostas ou que um método seja sempre superior ao outro. Inconsistências resultantes da aplicação de diferentes métodos estatísticos podem até aparecer em um único plano de execução final, embora raramente sejam notadas.

Como observação lateral, o estimador de cardinalidade do SQL Server 2014 adota uma abordagem diferente para combinar as informações do histograma ajustadas por filtro independente ( "alinhamento grosso" ), o que resulta em uma estimativa final diferente de 10.1006 linhas para esta consulta:

Plan for computation:

  CSelCalcExpressionComparedToExpression
  (QCOL: [s].SalesOrderID x_cmpEq QCOL: [d].SalesOrderID)

Loaded histogram for column QCOL: [s].SalesOrderID from stats with id 1
Loaded histogram for column QCOL: [d].SalesOrderID from stats with id 1

Stats collection generated: 

  CStCollJoin(ID=4, **CARD=10.1006** x_jtInner)
      CStCollFilter(ID=3, CARD=2.61972)
          CStCollBaseTable(ID=1, CARD=31465 TBL: Sales.SalesOrderHeader AS TBL: s)
      CStCollBaseTable(ID=2, CARD=121317 TBL: Sales.SalesOrderDetail AS TBL: d)

Isso acontece com o mesmo resultado que o cálculo na pergunta, embora o raciocínio detalhado seja diferente (ou seja, não é baseado em uma implementação de loops aninhados assumidos).

Paul White 9
fonte