Plany zapytań z filtrami bitmapowymi mogą być czasami trudne do odczytania. Z artykułu BOL dotyczącego strumieni podziału (wyróżnienie moje):
Operator strumieni partycji zużywa wiele strumieni i tworzy wiele strumieni rekordów. Zawartość i format rekordu nie ulegają zmianie. Jeśli optymalizator zapytań korzysta z filtra bitmapowego, liczba wierszy w strumieniu wyjściowym jest zmniejszona.
Ponadto pomocny jest także artykuł na temat filtrów bitmapowych:
Analizując plan wykonania zawierający filtrowanie bitmap, ważne jest zrozumienie, w jaki sposób dane przepływają przez plan i gdzie stosowane jest filtrowanie. Filtr bitmapy i zoptymalizowana mapa bitowa są tworzone po stronie kompilacji (tabela wymiarów) sprzężenia mieszającego; jednak faktyczne filtrowanie jest zwykle wykonywane w operatorze Parallelism, który znajduje się po stronie wejściowej sondy (tabela faktów) sprzężenia mieszającego. Jednak gdy filtr bitmapowy oparty jest na kolumnie z liczbą całkowitą, filtr można zastosować bezpośrednio do początkowej operacji skanowania tabeli lub indeksu zamiast operatora Parallelism. Ta technika nazywa się optymalizacją rzędową.
Wierzę, że to właśnie obserwujesz w swoim zapytaniu. Możliwe jest stworzenie stosunkowo prostej wersji demonstracyjnej, która pokazuje operator strumieni partycjonowania zmniejszający oszacowanie liczności, nawet gdy operator bitmapy jest IN_ROW
przeciwny tabeli faktów. Przygotowanie danych:
create table outer_tbl (ID BIGINT NOT NULL);
INSERT INTO outer_tbl WITH (TABLOCK)
SELECT TOP (1000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values;
create table inner_tbl_1 (ID BIGINT NULL);
create table inner_tbl_2 (ID BIGINT NULL);
INSERT INTO inner_tbl_1 WITH (TABLOCK)
SELECT (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) / 2000000 - 2) NUM
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
INSERT INTO inner_tbl_2 WITH (TABLOCK)
SELECT (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) / 2000000 - 2) NUM
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
Oto zapytanie, którego nie powinieneś uruchamiać:
SELECT *
FROM outer_tbl o
INNER JOIN inner_tbl_1 i ON o.ID = i.ID
INNER JOIN inner_tbl_2 i2 ON o.ID = i2.ID
OPTION (HASH JOIN, QUERYTRACEON 9481, QUERYTRACEON 8649);
Przesłałem plan . Spójrz na operatora w pobliżu inner_tbl_2
:
Pomocny może być również drugi test w Hash Joins on Nullable Columns autorstwa Paula White'a.
Istnieją pewne niespójności w sposobie stosowania redukcji wierszy. Widziałem to tylko w planie z co najmniej trzema stołami. Jednak zmniejszenie oczekiwanych wierszy wydaje się rozsądne przy właściwym rozmieszczeniu danych. Załóżmy, że połączona kolumna w tabeli faktów ma wiele powtarzanych wartości, które nie są obecne w tabeli wymiarów. Filtr bitmapowy może wyeliminować te wiersze, zanim dotrą do złączenia. W przypadku zapytania oszacowanie zostało zredukowane do 1. Sposób rozmieszczenia wierszy w funkcji skrótu stanowi dobrą wskazówkę:
Na tej podstawie podejrzewam, że masz wiele powtarzających się wartości dla Object1.Column21
kolumny. Jeśli powtarzające się kolumny nie znajdą się w histogramie statystyk, Object4.Column19
wówczas SQL Server może bardzo źle oszacować liczność.
Myślę, że powinieneś się martwić, że poprawienie wydajności zapytania może być możliwe. Oczywiście, jeśli zapytanie spełnia wymagania dotyczące czasu odpowiedzi lub SLA, dalsze badanie może nie być warte. Jeśli jednak chcesz dokładniej zbadać sprawę, możesz zrobić kilka rzeczy (oprócz aktualizacji statystyk), aby dowiedzieć się, czy optymalizator zapytań wybrałby lepszy plan, gdyby miał lepsze informacje. Można umieścić Wyniki dołączyć pomiędzy Database1.Schema1.Object10
i Database1.Schema1.Object11
do tabeli temp i zobacz, czy nadal się pętla zagnieżdżona łączy. Możesz zmienić to połączenie na LEFT OUTER JOIN
tak, aby optymalizator zapytań nie zmniejszył liczby wierszy na tym etapie. Możesz dodać MAXDOP 1
podpowiedź do swojego zapytania, aby zobaczyć, co się stanie. Możesz użyćTOP
wraz z tabelą pochodną, aby wymusić przejście do końca jako ostatnie, lub możesz nawet skomentować sprzężenie z zapytania. Mamy nadzieję, że te sugestie wystarczą, aby zacząć.
Jeśli chodzi o element Connect w pytaniu, jest bardzo mało prawdopodobne, że jest on powiązany z Twoim pytaniem. Ten problem nie dotyczy słabych oszacowań wierszy. Ma to związek z równoległym warunkiem wyścigu, który powoduje przetwarzanie zbyt wielu wierszy w planie zapytań za kulisami. Wygląda na to, że zapytanie nie wykonuje dodatkowej pracy.