Jak optymalizator SQL Server ocenia liczbę wierszy w połączonej tabeli?

13

Korzystam z tego zapytania w bazie danych 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

Jeśli spojrzę na szacowany plan wykonania, zobaczę:

wprowadź opis zdjęcia tutaj

Początkowe wyszukiwanie indeksu (prawy górny róg) korzysta z indeksu IX_SalesOrderHeader_CustomerID i przeszukuje dosłownie 11077. Szacuje się, że 2,6192 wierszy.

wprowadź opis zdjęcia tutaj

Jeśli DBCC SHOW_STATISTICS ('Sales.SalesOrderHeader', 'IX_SalesOrderHeader_CustomerID') WITH HISTOGRAMużyję, pokazuje, że wartość 11077 jest między dwoma próbkowanymi kluczami 11019 i 11091.

wprowadź opis zdjęcia tutaj

Średnia liczba różnych wierszy między 11019 a 11091 wynosi 2,619718 lub zaokrąglona do 2,61972, co jest wartością szacowanych wierszy pokazanych dla wyszukiwania indeksu.

Część, której nie rozumiem, to szacunkowa liczba wierszy dla wyszukiwania klastrowanego indeksu względem tabeli SalesOrderDetail.

wprowadź opis zdjęcia tutaj

Jeśli uruchomię DBCC SHOW_STATISTICS ('Sales.SalesOrderDetail', 'PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID'):

wprowadź opis zdjęcia tutaj

Zatem gęstość SalesOrderID (do której dołączam) wynosi 3.178134E-05. Oznacza to, że 1 / 3.178134E-05 (31465) jest równy liczbie unikalnych wartości SalesOrderID w tabeli SalesOrderDetail.

Jeśli w SalesOrderDetail znajduje się 31465 unikalnych identyfikatorów SalesOrderID, wówczas przy równomiernym rozkładzie średnia liczba wierszy przypadająca na SalesOrderID wynosi 121317 (całkowita liczba wierszy) podzielona przez 31465. Średnia wynosi 3,85561

Więc jeśli szacowana liczba wierszy do przejścia przez pętlę wynosi 2,61972, a średnia do zwrotu w 3,85561, pomyślałbym, że szacowana liczba wierszy wyniosłaby 2,61972 * 3,85561 = 10,10062.

Ale szacunkowa liczba wierszy wynosi 11,4867.

Myślę, że moje rozumienie drugiego oszacowania jest nieprawidłowe i wydaje się, że różne liczby wskazują na to. czego mi brakuje?

8kb
źródło

Odpowiedzi:

20

Myślę, że moje rozumienie drugiego oszacowania jest nieprawidłowe i wydaje się, że różne liczby wskazują na to. czego mi brakuje?

Za pomocą estymatora liczności kardynalności SQL Server 2012 selektywność łączenia steruje szacunkową liczbą wierszy po wewnętrznej stronie złączenia zagnieżdżonych pętli, a nie na odwrót.

Liczbę 11.4867 oblicza się (do wyświetlenia w showplan), dzieląc obliczoną szacunkową liczność wyniku łączenia (30.0919) przez liczbę iteracji (2.61972). Wynik, stosując arytmetykę zmiennoprzecinkową pojedynczej precyzji, wynosi 11,4867 .

To naprawdę jest tak proste. Zauważ, że (logiczna) selektywność łączenia jest niezależna od wyboru operatora łączenia fizycznego. Nie zmienia się to, czy połączenie zostanie ostatecznie wykonane za pomocą operatora fizycznego Zagnieżdżone Pętle, Skrót lub Połącz Połącz.

W SQL Server 2012 i wcześniejszych selektywność łączenia (jako całość) jest szacowana przy użyciu SalesOrderIDhistogramów z każdej tabeli (obliczana dla każdego kroku histogramu, po wyrównaniu granicy kroku za pomocą interpolacji liniowej, jeśli to konieczne). SalesOrderIDHistogram związany z SalesOrderHeadertabeli jest również dostosowana do efektu skalowania niezależnego CustomerIDfiltra.

Nie oznacza to, że jest coś zasadniczo „złego” w alternatywnym obliczeniu zaproponowanym w pytaniu; po prostu przyjmuje inny zestaw założeń. Zawsze będą różne sposoby obliczania lub łączenia oszacowań dla danej sekwencji operacji logicznych. Nie ma ogólnej gwarancji, że różne metody statystyczne zastosowane do tych samych danych dadzą te same odpowiedzi lub że jedna metoda zawsze będzie lepsza od drugiej. Niespójności wynikające z zastosowania różnych metod statystycznych mogą nawet pojawić się w ramach jednego ostatecznego planu wykonania, choć rzadko są zauważane.

Na marginesie, estymator liczności SQL Server 2014 stosuje inne podejście do łączenia informacji histogramu skorygowanego niezależnie przez filtr ( „wyrównanie zgrubne” ), co skutkuje innym końcowym oszacowaniem 10 1006 wierszy dla tego zapytania:

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)

Zdarza się, że jest to ten sam wynik, co obliczenie w pytaniu, chociaż szczegółowe uzasadnienie jest inne (tzn. Nie opiera się na założonej implementacji pętli zagnieżdżonych).

Paul White 9
źródło