Funkcje użytkownika

Funkcje użytkownika (User Defined Functions UDF) to obiekty programistyczne, które pozwalają znacząco rozszerzyć możliwości pisania kwerend.

Są one dość często stosowane – przedewszystkim w zapytaniach, dlatego postanowiłem umieścić ich opis w ramach tego kursu. Zakres funkcji w SQL, podobnie jak miejsc w których możemy je użyć jest naprawdę szeroki (procedury składowane, wartości domyślne, ograniczenia sprawdzające etc..). Rozdział ten ogranicza się tylko do funkcji użytkownika tworzonych w T-SQL w aspekcie pisania zapytań.

Obiekty te, podobnie jak w typowych językach programowania, mogą realizować rozmaite zadania. Używamy je do przetwarzania jakiś danych, celem uproszczenia zapisu kwerendy. Są opakowaniem logiki aplikacyjnej w wygodny obiekt i przeniesienie jej do warstwy serwera bazodanowego. Raz utworzone, mogą być stosowane w wielu miejscach.


Korzyści płynące ze stosowania funkcji użytkownika

  • poprawienie czytelności kodu i wygoda ich wielokrotnego użycia w różnych miejscach.
  • mogą zmniejszać ruch sieciowy – liczba danych (przesłanych bajtów) związanych z wywołaniem funkcji jest zazwyczaj znacznie mniejsza niż cała jej definicja, składowana na serwerze.
  • za pomocą funkcji CLR, można realizować zadania niemożliwe do wykonania w czystym T-SQL. Złożone obliczenia efektywniej jest wykonywać np. w obiektowych językach programowania.
  • mają możliwość wywołań rekurencyjnych.

Oczywiście jest też druga strona medalu. Funkcje zazwyczaj są kosztowne, szczególnie gdy wykonywane są dla każdego elementu niezależnie – skalarne (!). Z tego powodu bywają zmorą administratorów i są jednym z pierwszych punktów uwagi w momencie analizy wydajności zapytań. Ponadto dla funkcji tabelarycznych, szacowanie kardynalności zbioru zwykle jest wypaczone. Są traktowane jako black-box i przyjmowana wartość liczebności zbioru wynosi dla nich zawsze 1. Biorąc pod uwagę fakt, że za ich pomocą możemy wykonywać naprawdę skomplikowane działania – koszt ten czasem bywa akceptowalny. Jak widać odpowiedź na pytanie czy stosować funkcje czy nie, jak zwykle brzmi – to zależy 😉


Pisanie zapytań SQL z wykorzystaniem funkcji użytkownika

Funkcje ze swej definicji, mogą przyjmować parametry wejściowe i zwracają zawsze jakiś obiekt. W SQL, będzie to zawsze zbiór (tabela) lub wartość skalarna. Jeśli zaprotestujesz, że przecież wartość skalarna to też zbiór (jednoelementowy , opisany jedną kolumną) to znaczy, że uważnie przerabiałeś ten kurs lub po prostu dobrze czujesz koncepcję zbiorów.

Wyjątkowo w odniesieniu do funkcji, istnieje sztywny podział na takie, które zwracają wartości skalarne lub zbiory. Mamy więc do czynienia z dwoma głównymi rodzajami tych obiektów – odnajdziesz je w kategorii Programmability > Functions , w drzewie obiektów swojej bazy danych :

Funkcje_Uzytkownika_SQL

Ich strukturę, sposób tworzenia i przykłady zastosowań, opisuję szczegółowo w dedykowanych im artykułach :

  • FUNKCJE SKALARNE – zwracające pojedynczą wartość.
  • FUNKCJE TABELARYCZNE – zwracające zbiór. Wśród tych, rozróżniamy jeszcze dwa podtypy :
    • proste (inline table UDF)
    • złożone (multi-statement table valued functions)

Funkcje od strony programistycznej

Warto wiedzieć, że funkcje (zarówno wbudowane jak i użytkownika) możemy dodatkowo podzielić, ze względu na możliwość przewidzenia wyniku.

Deterministyczne to takie, które przyjmując określone parametry wejściowe, po każdym wywołaniu zwracają identyczny wynik. Są więc, można powiedzieć, z góry określone (łac. determinare – określić).
Przykładem takich funkcji jest większość wbudowanych matematycznych czy tekstowych, np. :

SELECT ABS(-2) as AbsVal, Left('Ala ma kota',3) as Lewy3
AbsVal      Lewy3
----------- -----
2           Ala

(1 row(s) affected)

Przy każdym wywołaniu funkcji wartości bezwzględnej ABS(), dla konkretnej wartości argumentu, równego np. -2, zwracany jest ten sam wynik. Podobnie jak w funkcji LEFT.

Z kolei funkcje niedeterministyczne, to takie, które pomimo tych samych wartości parametrów na wejściu, zwracają za każdym razem inny wynik. Przykładem może być funkcja NEWID(), generująca kolejny unikalny identyfikator czy GETDATE(), zwracająca datę i czas systemowy.

Select NEWID() as UniqueIdentifier, GETDATE() as DataCzas
UniqueIdentifier                     DataCzas
------------------------------------ -----------------------
EBF78558-F7E7-4A91-A1DA-17245C9CAA68 2014-03-06 09:31:25.893

(1 row(s) affected)

Co prawda powyższe, przykładowe funkcje nie przyjmują parametrów, ale przecież brak parametru to też konkretny przypadek wywołania.

Determinizm lub jego brak jest o tyle istotny, że w przypadku tych pierwszych, możliwe jest zastosowanie indeksów np. w widokach, których wartości kolumn są wyliczane na podstawie takich funkcji. Ponadto jest szansa, że funkcja deterministyczna zostanie wykonana tylko raz (np. GETDATE()) dla wszystkich rekordów.

Są to aspekty z zakresu projektowania i programowania baz danych i poruszam je w detalach na szkoleniach programistycznych.

3 Responses

  • Chciałabym zapytać o tworzenie funkcji, gdzie nie trzeba wpisywać całej nazwy, tylko jej część
    where Pracownik_Nazwisko like '@nazw%’

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.