Table Valued Function Killing My Query Performance

Miałem dziś straszny czas, próbując uzyskać zapytanie, aby wykonać tak, jak bym się spodziewał. Musiałem wprowadzić niewielką zmianę do funkcji wartości tabeli, która żyje w zapytaniu wczoraj i ta zmiana stworzyła ogromny wpływ na wydajność zapytania. Po ocenie planu wykonania i patrząc na statystyki IO i czasu okazało się, że ponieważ zmieniłem funkcję zwracania zmiennej tabeli zamiast tylko zestaw wyników, to robi Pełne skanowanie na jednej z tabel jest queried.

Moje pytanie brzmi dlaczego zwrócenie tabeli (TableVariable) zamiast tylko Select / Result set powoduje tak dużą zmianę w planie?

...

Author: frictionlesspulley, 2010-11-05

5 answers

Zwrócenie zmiennej Tabelkowej sprawi, że będzie to funkcja wielostanowiskowa o wartości tabeli i może być niekorzystne dla wydajności ze względu na fakt, że jest traktowana jak Tabela, z wyjątkiem tego, że nie ma statystyk dostępnych dla SQL Server, aby oprzeć dobry plan wykonania - więc oszacuje funkcję jako zwracającą bardzo małą liczbę wierszy. Jeśli zwróci większą liczbę wierszy, wtedy wygenerowany plan może być o wiele mniej niż optymalny.

Natomiast zwracanie tylko SELECT sprawia, że jest to inline table valued function - pomyśl o tym bardziej jak o widoku. W takim przypadku rzeczywiste tabele bazowe zostają przeniesione do głównego zapytania i można wygenerować lepszy plan realizacji na podstawie odpowiednich statystyk. Zauważysz, że w tym przypadku plan wykonania w ogóle nie będzie zawierał wzmianki o funkcji, ponieważ jest to w zasadzie scalenie funkcji do głównego zapytania.

Jest na nim świetna Referencja MSDN autorstwa inżynierów CSS SQL Server, w tym "cytat": {]}

Ale jeśli używasz multi-statement TVF, jest traktowany jak inny stolik. Bo nie ma statystyki dostępne, SQL Server ma aby poczynić pewne założenia i w ogólne zapewniają niskie oszacowanie. Jeśli TVF zwraca tylko kilka wierszy, będzie będzie dobrze. Ale jeśli zamierzasz wypełniać TVF tysiącami rows i jeśli ten TVF jest połączony z innych tabel, nieefektywny plan może wynik z niskiej oceny kardynalności.

 49
Author: AdaTheDev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-05 19:33:17

Dzieje się tak dlatego, że wartość tabeli z wieloma instrukcjami UDF nie może być przetwarzana w linii z resztą stanu SQL, w którym jest używana, i dlatego nie może być częścią planu Cache instrukcji.. Oznacza to, że musi być skompilowany oddzielnie od reszty SQL, w którym jest używany, w kółko, dla każdego wiersza w wynikowym zestawie wygenerowane przez zapytanie.

An Inline Tabela wartości UDF, otoh, jest przetwarzany i kompilowany wraz z sql, w którym jest używany, i staje się więc część planu cache i tylko jest przetwarzany i kompilowany raz, bez względu na to, ile wierszy wygenerujesz.

 5
Author: Charles Bretana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-11 18:22:52

Naprawdę niemożliwe, aby odpowiedzieć definitywnie bez więcej informacji. Jednak, ponieważ lubię brać szalone pchnięcia w ciemności . . .

Zmienne tabeli nie mogą być optymalizowane przez silnik--silnik zawsze "zakłada", że zmienna tabeli ma tylko jeden wiersz, gdy generuje plan wykonania. To jest jeden z powodów, dla których możesz widzieć dziwne wyniki.

 3
Author: Phil Sandler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-05 19:09:41

W przypadku użycia UDF o wartości tabeli z wieloma instrukcjami, UDF jest uruchamiany do końca, zanim jego wyniki będą mogły zostać użyte przez wywołującego. Dzięki wbudowanemu w tabelę UDF serwer SQL zasadniczo rozszerza UDF do wywołującego zapytanie, podobnie jak rozszerzenie makra . Ma to m.in. następujące implikacje:

  • klauzula WHERE wywołującego zapytanie może być interpolowana bezpośrednio do wartościowego w wierszu tabeli UDF, ale nie do wielostanowiskowego UDF. Tak więc, jeśli twój wartościowy UDF generuje wiele wierszy, które byłyby filtrowane przez klauzulę WHERE wywołującego zapytanie, optymalizator zapytań może zastosować klauzulę WHERE bezpośrednio do wartościowego w tabeli wiersza UDF, ale nie do wielozadaniowego UDF.
  • inline table-valued UDF zachowuje się tak, jak sparametryzowany VIEW, gdyby SQL Server miał taką koncepcję, podczas gdy multi-statement table-valued UDF zachowywałby się tak, jakbyś wypełnił, a następnie użył zmiennej table w zapytaniu.

Jeśli twój UDF zwraca wiele wierszy i jest wsparty na stole, wyobrażam sobie, że to może być miejsce, skąd pochodzi skanowanie tabeli. Albo dodaj więcej parametrów do swojego UDF, aby umożliwić wywołującemu ograniczenie jego rozmiaru wyniku, albo spróbuj przeformułować go jako wartościowy w tabeli UDF z pomocą znajomych, takich jak UNION et al. Za wszelką cenę unikałbym wielostanowiskowych, wycenianych w tabeli UDFs, chyba że rozmiar wyniku jest znany tylko z kilku wierszy i , trudno jest uzyskać wymagane wyniki za pomocą logiki opartej na zestawach.

 0
Author: binki,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-19 19:10:52

Na SQL Server 2014 udało nam się rozwiązać nasz problem, wstawiając dane funkcji wartości tabeli do tabeli temp, a następnie wykonując join na niej. Zamiast łączenia bezpośrednio z funkcją wartości tabeli.

Poprawiło to czas wykonania z 2 min do 4 sek. Oto przykład, który zadziałał w naszym zespole:

-- SLOW QUERY (2 min):

DECLARE @id INT = 1;

SELECT * 
FROM [data].[someTable] T
INNER JOIN [data].[tableValueFunction](@id) TVF ON TVF.id = T.id;

-- FAST QUERY (4 sec):

DECLARE @id INT = 1;

SELECT * 
INTO #tableValueFunction
FROM [data].[tableValueFunction](@id) TVF

SELECT * 
FROM [data].[someTable] T
INNER JOIN #tableValueFunction TVF ON TVF.id = T.id;
 0
Author: Nanuz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-31 14:32:48