Funkcje tabeli
Na życie wykonuję bardzo szybkie, złożone migracje baz danych, używając SQL jako języka klienta i serwera (żaden inny język nie jest używany), wszystko działa po stronie serwera, gdzie kod rzadko wychodzi z silnika bazy danych. Funkcje tabeli odgrywają OGROMNĄ rolę w mojej pracy . Nie używam „kursorów”, ponieważ są zbyt wolne, aby spełnić moje wymagania dotyczące wydajności, a wszystko, co robię, jest zorientowane na zestaw wyników. Funkcje tabel bardzo mi pomogły w całkowitym wyeliminowaniu użycia kursorów, osiągnięciu bardzo dużej szybkości i znacznie przyczyniły się do zmniejszenia objętości kodu i poprawy prostoty.
Krótko mówiąc, używasz zapytania odwołuje się do dwóch (lub więcej) funkcji tabelowych w celu przekazania danych z jednej funkcji tabelowej do następnej. Zestaw wyników zapytania wybierającego, który wywołuje funkcje tabeli, służy jako kanał do przekazywania danych z jednej funkcji tabeli do drugiej. Na platformie DB2 / wersji, nad którą pracuję, i po szybkim spojrzeniu na podręcznik Postgres 9.1, wygląda na to, że tam jest to samo, możesz przekazać tylko jeden wiersz wartości kolumny jako dane wejściowe do dowolnego wywołania funkcji tabeli, jak odkryłeś. Jednakże wywołanie funkcji tabeli ma miejsce w środku przetwarzania zestawu wyników zapytania, uzyskujesz ten sam efekt przekazywania całego zestawu wyników do każdego wywołania funkcji tabeli, aczkolwiek w przypadku instalacji wodociągowej silnika bazy danych dane są przekazywane tylko jeden wiersz naraz do każdej funkcji tabeli.
Funkcje tabelowe akceptują jeden wiersz kolumn wejściowych i zwracają pojedynczy zestaw wyników z powrotem do zapytania wywołującego (tj. Select), które wywołało funkcję. Kolumny zestawu wyników przekazane z funkcji tabelowej stają się częścią zestawu wyników zapytania wywołującego i dlatego są dostępne jako dane wejściowe do następnej funkcji tabelowej , do którego odwołuje się później w tej samej kwerendzie, zwykle jako kolejne sprzężenie. Kolumny wyników pierwszej funkcji tabelowej są przekazywane jako dane wejściowe (jeden wiersz naraz) do drugiej funkcji tabelowej, która zwraca kolumny zestawu wyników do zestawu wyników zapytania wywołującego. Zarówno pierwsza, jak i druga kolumna zestawu wyników funkcji tabelowej są teraz częścią zestawu wyników zapytania wywołującego i są teraz dostępne jako dane wejściowe (jeden wiersz naraz) trzeciej funkcji tabelowej. Każde wywołanie funkcji tabeli rozszerza zbiór wyników wywołania zapytania o zwracane kolumny. Może to trwać, dopóki nie zaczniesz osiągać limitów szerokości zestawu wyników, które prawdopodobnie różnią się w zależności od silnika bazy danych.
Rozważmy ten przykład (który może nie odpowiadać wymaganiom składniowym lub możliwościom Postgresa, gdy pracuję nad DB2). Jest to jeden z wielu wzorców projektowych, w których używam funkcji tabeli, jest jednym z prostszych, który moim zdaniem jest bardzo ilustracyjny i który, jak sądzę, będzie się podobał, jeśli funkcje tabelowe były intensywnie używane w głównym nurcie (o ile wiem, nie są, ale myślę, że zasługują na więcej uwagi niż dostają).
W tym przykładzie używane funkcje tabeli to:VALIDATE_TODAYS_ORDER_BATCH, POST_TODAYS_ORDER_BATCH i DATA_WAREHOUSE_TODAYS_ORDER_BATCH. W wersji DB2, nad którą pracuję, zawijasz funkcję tabeli wewnątrz „TABLE(tutaj umieść wywołanie funkcji tabeli i jej parametry)”, ale po szybkim spojrzeniu na podręcznik Postgresa wydaje się, że pomijasz opakowanie „TABLE()”.
create table TODAYS_ORDER_PROCESSING_EXCEPTIONS as (
select TODAYS_ORDER_BATCH.*
,VALIDATION_RESULT.ROW_VALID
,POST_RESULT.ROW_POSTED
,WAREHOUSE_RESULT.ROW_WAREHOUSED
from TODAYS_ORDER_BATCH
cross join VALIDATE_TODAYS_ORDER_BATCH ( ORDER_NUMBER, [either pass the remainder of the order columns or fetch them in the function] )
as VALIDATION_RESULT ( ROW_VALID ) --example: 1/0 true/false Boolean returned
left join POST_TODAYS_ORDER_BATCH ( ORDER_NUMBER, [either pass the remainder of the order columns or fetch them in the function] )
as POST_RESULT ( ROW_POSTED ) --example: 1/0 true/false Boolean returned
on ROW_VALIDATED = '1'
left join DATA_WAREHOUSE_TODAYS_ORDER_BATCH ( ORDER_NUMBER, [either pass the remainder of the order columns or fetch them in the function] )
as WAREHOUSE_RESULT ( ROW_WAREHOUSED ) --example: 1/0 true/false Boolean returned
on ROW_POSTED = '1'
where coalesce( ROW_VALID, '0' ) = '0' --Capture only exceptions and unprocessed work.
or coalesce( ROW_POSTED, '0' ) = '0' --Or, you can flip the logic to capture only successful rows.
or coalesce( ROW_WAREHOUSED, '0' ) = '0'
) with data
- Jeśli tabela TODAYS_ORDER_BATCH zawiera 1 000 000 wierszy, wówczas VALIDATE_TODAYS_ORDER_BATCH zostanie wywołana 1 000 000 razy, raz dla każdego wiersza.
- Jeśli 900 000 wierszy przejdzie weryfikację wewnątrz VALIDATE_TODAYS_ORDER_BATCH, POST_TODAYS_ORDER_BATCH zostanie wywołane 900 000 razy.
- Jeśli tylko 850 000 wierszy zostanie pomyślnie wysłany, wówczas VALIDATE_TODAYS_ORDER_BATCH wymaga zamknięcia kilku luk LOL, a DATA_WAREHOUSE_TODAYS_ORDER_BATCH zostanie wywołane 850 000 razy.
- Jeśli 850 000 wierszy pomyślnie trafiło do hurtowni danych (tj. nie zostały wygenerowane żadne dodatkowe wyjątki), tabela TODAYS_ORDER_PROCESSING_EXCEPTIONS zostanie wypełniona 1 000 000 - 850 000 =150 000 wierszy wyjątków.
Wywołania funkcji tabeli w tym przykładzie zwracają tylko jedną kolumnę, ale mogą zwracać wiele kolumn. Na przykład funkcja tabelowa walidująca wiersz zamówienia może zwrócić przyczynę niepowodzenia walidacji zamówienia.
W tym projekcie praktycznie wszystkie rozmowy między HLL a bazą danych są wyeliminowane, ponieważ requester HLL prosi bazę danych o przetworzenie całej partii w JEDNYM żądaniu. Powoduje to redukcję milionów zapytań SQL do bazy danych, OGROMNE usunięcie milionów wywołań procedur lub metod HLL, aw rezultacie zapewnia OGROMNE usprawnienie środowiska wykonawczego. W przeciwieństwie do tego, starszy kod, który często przetwarza jeden wiersz na raz, zazwyczaj wysyłał 1 000 000 żądań pobrania SQL, 1 dla każdego wiersza w TODAYS_ORDER_BATCH, plus co najmniej 1 000 000 żądań HLL i/lub SQL w celu weryfikacji, plus co najmniej 1 000 000 HLL i /lub żądania SQL do celów księgowania plus 1 000 000 żądań HLL i/lub SQL w celu wysłania zamówienia do hurtowni danych. To prawda, że przy użyciu tej konstrukcji funkcji tabelowej wewnątrz funkcji tabelowych żądania SQL są wysyłane do bazy danych, ale gdy baza danych wysyła żądania do siebie (tj. z wnętrza funkcji tabelowej), żądania SQL są obsługiwane znacznie szybciej (zwłaszcza w porównaniu z starszy scenariusz, w którym requester HLL wykonuje przetwarzanie pojedynczego wiersza ze zdalnego systemu, z najgorszym przypadkiem w sieci WAN - OMG, nie rób tego).
Możesz łatwo napotkać problemy z wydajnością, jeśli użyjesz funkcji tabeli do „pobrania zestawu wyników”, a następnie dołączenia tego zestawu wyników do innych tabel. W takim przypadku optymalizator SQL nie może przewidzieć, jaki zestaw wierszy zostanie zwrócony z funkcji tabeli, a zatem nie może zoptymalizować sprzężenia z kolejnymi tabelami. Z tego powodu rzadko używam ich do pobierania zestawu wyników, chyba że wiem, że zestaw wyników będzie zawierał bardzo małą liczbę wierszy, a tym samym nie spowoduje problemów z wydajnością lub nie muszę dołączać do kolejnych tabel.
Moim zdaniem jednym z powodów, dla których funkcje tabel nie są w pełni wykorzystywane, jest to, że często są one postrzegane jedynie jako narzędzie do pobierania zestawu wyników, które często działa słabo, więc są spisywane jako „słabe” narzędzie w użyciu.
Funkcje tabel są niezwykle przydatne do przekazywania większej liczby funkcji na serwer, do eliminowania większości rozmów między serwerem bazy danych a programami na zdalnych systemach, a nawet do eliminowania rozmów między serwerem bazy danych a programami zewnętrznymi na tym samym serwerze. Nawet rozmowy między programami na tym samym serwerze wiążą się z większym obciążeniem, niż wielu ludziom wydaje się, i wiele z nich jest niepotrzebnych. Sedno funkcji tabelowych polega na używaniu ich do wykonywania działań w ramach przetwarzania zbioru wyników.
Istnieją bardziej zaawansowane wzorce projektowe do korzystania z funkcji tabelowych, które opierają się na powyższym wzorcu, w których można jeszcze bardziej zmaksymalizować przetwarzanie zestawu wyników, ale ten post jest już bardzo do przyswojenia dla większości.