Układanka optymalizatora zapytań SQL Server 2008 R2
Mamy dwie tabele, każda zawierająca 9 milionów wierszy. 70 000 wierszy jest różnych, pozostałe są takie same.
To jest szybkie, 13 sekund,
select * from bigtable1
except select * from similar_bigtable2
To sortuje dane wyjściowe i jest również szybkie, również 13 sekund,
select * into #q from bigtable1
except select * from similar_bigtable2
select * from #q order by sort_column
Chociaż jest to niezwykle powolne:
;with q as (
select * from bigtable1
except select * from similar_bigtable2
)
select * from q order by sort_column
Nawet „sztuczka”, której czasem używam, aby zasugerować programowi SQL Server, że musi wstępnie obliczyć określoną część zapytania, zanim przejdzie dalej, nie działa i powoduje również wolne zapytanie:
;with q as (
select top 100 percent * from bigtable1
except select * from similar_bigtable2
)
select * from q order by sort_column
Patrząc na plany zapytań, nie jest trudno znaleźć przyczynę:
SQL Server umieszcza dwa rodzaje 9 milionów wierszy przed hashatchem, podczas gdy wolałbym, aby dodał tylko jeden rodzaj 70 000 wierszy po hashatchu.
Więc pytanie: jak mogę polecić optymalizatorowi zapytań, aby to zrobił?
źródło
EXCEPT
(np.OUTER JOIN
)? Zdaję sobie sprawę, że składnia jest mniej wygodna, ale możesz lepiej grać ze wskazówkami dotyczącymi indeksowania / dołączania (lub nie musisz). Alternatywą, której teraz używasz (najpierw wstawianie do tabeli temperatur) jest obejście w ostateczności, ale w niektórych przypadkach jest to jedyny sposób, aby zmusić optymalizator do całkowitego oddzielenia dwóch części zapytania w pożądany sposób.Odpowiedzi:
Główna różnica między tymi dwoma planami zapytań polega na różnicy między dopasowaniem mieszania i połączeniem scalającym. Dopasowywanie skrótów jest bardziej wydajne i jak widać, zapytanie działa szybciej w opcji 1 (bez użycia CTE).
CTE jest doskonałym narzędziem, ale wydaje się, że nie jest wydajne w dwóch przypadkach, skomplikowanych predykatach lub nieunikalnym kluczu nadrzędnym / podrzędnym. W twoim przypadku nie ma unikalnego klucza, a serwer SQL musi najpierw posortować zestawy danych, aby spełnić twoje wymagania. Spójrz na poniższy link, który mówi ci więcej na ten temat: http://blogs.msdn.com/b/sqlcat/archive/2011/04/28/optimize-recursive-cte-query.aspx
Wygląda więc na to, że musisz zaakceptować jego powolność lub przepisać logikę pętlą WHILE, która może być bardziej wydajna.
źródło
Spróbuj tego, jeszcze lepiej?
źródło
To nie jest idealne rozwiązanie, ale jeśli nie jesteś w stanie zbudować tsql w celu wygenerowania wydajnego planu, możesz ustawić przewodnik po planach, aby wymusić żądany plan. Może to oznaczać, że jeśli dostępny będzie bardziej wydajny plan, SQL go nie rozważy, ale jest to opcja.
źródło