Data: 1.01.2026

Tworzenie funkcji skalarnych (UDF) w SQL Server
Spis treści
- Tworzenie funkcji skalarnych (UDF) w SQL Server
- Tworzenie funkcji skalarnych (UDF) w SQL Server
- Czym jest funkcja użytkownika w SQL Server
- Rodzaje funkcji użytkownika w SQL Server
- Składnia i tworzenie funkcji skalarnej w SQL Server
- Folder w SSMS przechowujący funkcje skalarne
- Funkcja skalarna w
SELECT– obliczanie ceny brutto - Funkcja skalarna w
SELECT– obliczanie dni roboczych - Funkcja skalarna w klauzuli
SELECT– obliczanie dni roboczych z wykluczeniem świąt - Wykorzystywanie skalarnych funkcji użytkownika w zapytaniach SQL
- Modyfikowanie i usuwanie własnych funkcji skalarnych
- Wyzwania wydajnościowe funkcji skalarnych
W tym artykule wyjaśnię, jak pisać skalarne funkcje użytkownika (UDF) w SQL Server. Pokażę Ci przykłady własnych funkcji, za pomocą których usprawnisz pracę z danymi.
Tworzenie funkcji skalarnych (UDF) w SQL Server
Skalarne funkcje użytkownika (ang. Scalar-valued Functions) to najprostszy typ funkcji, który dla wprowadzonych parametrów (lub bez nich) zwraca pojedynczą wartość określonego typu danych. Funkcja może zwracać np.
- liczbę (INT, DECIMAL)
- ciąg znaków (VARCHAR, CHAR)
- datę (DATE, DATETIME)
Funkcje skalarne działają podobnie do wbudowanych funkcji systemowych, takich jak UPPER(), LEN() czy ROUND(). Możesz ich używać w każdym miejscu zapytania SQL, gdzie oczekiwana jest pojedyncza wartość np.:
- na liście kolumn w instrukcji
SELECT - w warunkach filtrowania w klauzuli
WHERE - wewnątrz instrukcji
CASE - w definicjach kolumn obliczanych
Czym jest funkcja użytkownika w SQL Server
Funkcja użytkownika (User Defined Function) w SQL Server to obiekt bazy danych, który umożliwia zdefiniowanie własnej logiki obliczeniowej i jej wielokrotne wykorzystanie w zapytaniach SQL.
Funkcja może:
- przyjmować parametry wejściowe
- wykonywać określone operacje na danych
- zwracać wynik w postaci pojedynczej wartości (wartość skalarna) lub zestawu danych (tabela).
Dzięki temu te same obliczenia nie muszą być za każdym razem zapisywane od nowa w różnych miejscach kodu.
Tworzenie funkcji skalarnych (UDF) w SQL Server pozwoli Ci uniknąć wielokrotnego kopiowania tych samych fragmentów kodu. Wewnątrz funkcji możesz zamknąć złożoną logikę biznesową lub skomplikowane obliczenia, a następnie wywoływać je w różnych miejscach zapytania.
Główne zapytania stają się krótsze i bardziej czytelne, gdyż skomplikowane operacje ukryte są pod jedną nazwą funkcji. Dodatkowo, takie podejście ułatwia utrzymanie bazy danych. Jeśli zajdzie potrzeba zmiany algorytmu, zmodyfikujesz go tylko w jednym miejscu (w funkcji), a zmiana będzie widoczna we wszystkich skryptach, które z tej funkcji korzystają.
Rodzaje funkcji użytkownika w SQL Server
Ze względu na rodzaj zwracanego wyniku funkcje użytkownika dzielimy na:
- funkcje skalarne – zwracają pojedynczą wartość zgodną z typem zdefiniowanym w klauzuli
RETURNS - funkcje tabelaryczne – w wyniku działania zwracają tabelę. Możemy ich używać bezpośrednio w klauzuli
FROM.
Informacje na temat funkcji tabelarycznych znajdziesz w artykule Tworzenie funkcji tabelarycznych w SQL Server
Składnia i tworzenie funkcji skalarnej w SQL Server
Aby utworzyć funkcję skalarną musimy:
- określić typ zwracanych danych w klauzuli
RETURNS - podać w ciele funkcji pomiędzy
BEGINiENDjakie obliczenia będą wykonywane - zwrócić wynik za pomocą instrukcji
RETURN
Przykład funkcji skalarnej, która oblicza cenę brutto
-- 1. Nazwa funkcji (razem ze schematem np. dbo)CREATE FUNCTION [dbo].[ObliczBrutto]( -- 2. Argument (Parametry wejściowe) -- Nazwa parametru | Typ danych @CenaNetto DECIMAL(10,2), @StawkaVat DECIMAL(4,2))-- 3. Typ wyniku (co funkcja zwróci)RETURNS DECIMAL(10,2)ASBEGIN -- 4. Logika (ciało funkcji) DECLARE @Wynik DECIMAL(10,2);
SET @Wynik = @CenaNetto * (1 + @StawkaVat);
-- 5. Wynik (zwracana przez funkcję wartość) RETURN @Wynik;ENDGOWyjaśnienie:
CREATE FUNCTIONdbo.ObliczBrutto – tworzymy funkcję w schemacie dbo i nadajemy jej nazwę „ObliczBrutto”. Podczas tworzenia funkcji nie musimy podawać schematu, ale przy wywoływaniu funkcji podanie schematu jest niezbędne.- podajemy argumenty (parametry wejściowe) – „@CenaNetto” – to nazwa parametru, a „DECIMAL(10,2)” to typ danych. Znak małpy (@) umieszczony przed nazwą oznacza, że mamy do czynienia ze zmienną lokalną lub parametrem
RETURNSDECIMAL(10,2) – określamy, jaki typ danych funkcja zwróciAS BEGIN ... END– definiuje ciało funkcji, w którym zawarta jest cała logika działania funkcji. Wewnątrz:- deklarujemy zmienną pomocniczą o nazwie „Wynik” (
DECLARE@Wynik) - wykonujemy obliczenia i przypisujemy wynik obliczeń do zmiennej pomocniczej (
SET) - za pomocą instrukcji
RETURNzwracamy wartość wyliczeń jako wynik działania funkcji.
- deklarujemy zmienną pomocniczą o nazwie „Wynik” (
Folder w SSMS przechowujący funkcje skalarne
Funkcje skalarne widoczne są w SQL Server Management Studio w folderze Scalar-valued Functions:

Funkcja skalarna w SELECT – obliczanie ceny brutto
Aby wywołać funkcję skalarną w klauzuli SELECT wpisujemy nazwę funkcji oraz parametry. W przykładzie poniżej jako parametry
podajemy wartość z kolumny Freight w tabeli Orders oraz „0.23” jako wartość podatku VAT. Funkcja przetwarza dane z każdego wiersza i wyświetla wynik w nowej kolumnie.
SELECT OrderID, CustomerID, Freight AS CenaNetto, -- Wywołanie funkcji dbo.ObliczBrutto(Freight, 0.23) AS FreightBruttoFROM Orders;Wynik zapytania:
| OrderID | CustomerID | CenaNetto | FreightBrutto |
|---|---|---|---|
| 10248 | VINET | 32,38 | 39.83 |
| 10249 | TOMSP | 11,61 | 14.28 |
| 10250 | HANAR | 65,83 | 80.97 |
| 10251 | VICTE | 41,34 | 50.85 |
Funkcja skalarna w SELECT – obliczanie dni roboczych
W poniższym przykładzie tworzymy funkcję, za pomocą której wyliczmy dni robocze od poniedziałku do piątku między dwiema datami.
CREATE FUNCTION dbo.DniRobocze( @DataOd DATE, @DataDo DATE)RETURNS INTASBEGIN DECLARE @Licznik INT = 0;
-- zamieniamy daty jeśli ktoś poda odwrotnie IF @DataOd > @DataDo BEGIN DECLARE @Tmp DATE = @DataOd; SET @DataOd = @DataDo; SET @DataDo = @Tmp; END;
WHILE @DataOd <= @DataDo BEGIN -- 1 = niedziela, 7 = sobota (przy domyślnym DATEFIRST) IF DATEPART(WEEKDAY, @DataOd) NOT IN (1, 7) SET @Licznik += 1;
SET @DataOd = DATEADD(DAY, 1, @DataOd); END;
RETURN @Licznik;END;GOWyjaśnienie funkcji:
-
tworzymy w schemacie dbo funkcję o nazwie DniRobocze
-
funkcja przyjmuje dwa parametry typu DATE – @DataOd – data początkowa i @DataDo – data końcowa
-
funkcja zwraca wartość typu INT – liczbę dni roboczych
-
zmienna @Licznik przechowuje liczbę dni roboczych. Początkowa wartość to 0
-
w bloku
IF @DataOd > @DataDoBEGINDECLARE @Tmp DATE = @DataOd;SET @DataOd = @DataDo;SET @DataDo = @Tmp;END;funkcja sprawdza, czy użytkownik nie podał dat w złej kolejności. Jeśli @DataOd jest późniejsza niż @DataDo, daty są zamieniane miejscami za pomocą zmiennej tymczasowej @Tmp
-
w bloku
WHILE @DataOd <= @DataDoBEGIN-- 1 = niedziela, 7 = sobota (przy domyślnym DATEFIRST)IF DATEPART(WEEKDAY, @DataOd) NOT IN (1, 7)SET @Licznik += 1;SET @DataOd = DATEADD(DAY, 1, @DataOd);END;pętla sprawdza aktualną datę (@DataOd), ustala, czy data to dzień roboczy od poniedziałku do piątku. Jeśli data to dzień roboczy zwiększana jest wartość w zmiennej @Licznik. Pętla powtarza się dla każdego wiersza aż do daty w zmiennej @DataDo. Na końcu zawsze przesuwa datę o 1 dzień RETURN zwraca wynik znajdujący się w zmiennej @Licznik
Sprawdzenie działania funkcji:
SELECT dbo.DniRobocze('20260101','20260109')--Wynik: 7Funkcja skalarna w klauzuli SELECT – obliczanie dni roboczych z wykluczeniem świąt
W praktyce liczą się dni robocze – z pominięciem weekendów oraz świąt ustawowo wolnych od pracy. To szczególnie istotne przy wyliczaniu terminów realizacji, czasów obsługi zgłoszeń (SLA), harmonogramów projektów czy analiz kadrowych.
W poniższym przykładzie pokazuję, w jaki sposób wyliczyć dni robocze z pominięciem świąt oraz dni ustawowo wolnych od pracy. Funkcja przyjmuje zakres dat i zwraca liczbę dni roboczych, automatycznie pomijając soboty i niedziele oraz daty zapisane w tabeli ze świętami.
Zastosowanie osobnej tabeli na święta pozwala zarządzać kalendarzem – dodanie nowego dnia wolnego nie wymaga zmiany kodu funkcji, a jedynie uzupełnienia danych w tabeli świąt.
--Tworzenie tabeli ze świętami
CREATE TABLE dbo.Swieta( DataSwieta DATE PRIMARY KEY, Opis NVARCHAR(100));
Uzupełnianie świąt w tabeli
INSERT INTO dbo.Swieta (DataSwieta, Opis)VALUES('2026-01-01', 'Nowy Rok'),('2026-01-06', 'Trzech Króli');
CREATE FUNCTION dbo.DniRoboczePomSwieta( @DataOd DATE, @DataDo DATE)RETURNS INTASBEGIN DECLARE @Licznik INT = 0;
-- zamiana dat jeśli zostaną podane odwrotnie IF @DataOd > @DataDo BEGIN DECLARE @Tmp DATE = @DataOd; SET @DataOd = @DataDo; SET @DataDo = @Tmp; END;
WHILE @DataOd <= @DataDo BEGIN -- pon–pt IF DATEPART(WEEKDAY, @DataOd) NOT IN (1, 7) AND NOT EXISTS ( SELECT * FROM dbo.Swieta s WHERE s.DataSwieta = @DataOd ) BEGIN SET @Licznik += 1; END;
SET @DataOd = DATEADD(DAY, 1, @DataOd); END;
RETURN @Licznik;END;GOWyjaśnienie przykładu:
-
poleceniem
CREATE TABLEtworzymy nową tabelę o nazwie „Swieta” w schemacie dbo -
poleceniem
INSERT INTOwstawiamy do tabeli „Swieta” dwie daty -
tworzymy funkcję o nazwie DniRoboczePomSwieta w schemacie dbo
-
funkcja przyjmuje dwa parametry typu DATE – @DataOd – data początkowa i @DataDo – data końcowa
-
funkcja zwraca wartość typu INT – liczbę dni roboczych
-
zmienna @Licznik przechowuje liczbę dni roboczych. Początkowa wartość to 0
-
w bloku
IF @DataOd > @DataDoBEGINDECLARE @Tmp DATE = @DataOd;SET @DataOd = @DataDo;SET @DataDo = @Tmp;END;funkcja sprawdza, czy użytkownik nie podał dat w złej kolejności. Jeśli @DataOd jest późniejsza niż @DataDo, daty są zamieniane miejscami za pomocą zmiennej tymczasowej @Tmp
-
w bloku
WHILE @DataOd <= @DataDoBEGIN-- pon–ptIF DATEPART(WEEKDAY, @DataOd) NOT IN (1, 7)AND NOT EXISTS (SELECT *FROM dbo.Swieta sWHERE s.DataSwieta = @DataOd)BEGINSET @Licznik += 1;END;SET @DataOd = DATEADD(DAY, 1, @DataOd);END;pętla
WHILE- przechodzi dzień po dniu przez wskazany zakres dat i działa tak długo, jak długo zmienna @DataOd jest mniejsza lub równa @DataDo
- dla każdej kolejnej daty sprawdza, czy dany dzień przypada od poniedziałku do piątku oraz czy nie występuje w tabeli Swieta jako dzień świąteczny. Jeśli obydwa warunki są spełnione, zwiększana jest wartość licznika zmiennej @Licznik, co oznacza zliczenie dnia roboczego.
- na końcu każdej iteracji pętli data początkowa jest przesuwana o jeden dzień do przodu za pomocą funkcji
DATEADD(), co pozwala pętli przejść do sprawdzenia kolejnego dnia
Wykorzystywanie skalarnych funkcji użytkownika w zapytaniach SQL
Funkcje skalarne możemy stosować m.in. w klauzulach SELECT, WHERE, ORDER BY, GROUP BY, HAVING, JOIN, a także wewnątrz wyrażeń CASE i definicji kolumn obliczeniowych.
Poniżej przykładowa funkcja oraz jej wykorzystanie w klauzulach SQL
W przykładzie tworzymy funkcję, która przelicza cenę netto na cenę brutto przy założeniu 23-procentowej stawki VAT.
CREATE FUNCTION dbo.CalculateGrossPrice (@NetPrice MONEY)RETURNS MONEYASBEGIN -- Zwracamy cenę powiększoną o 23% VAT RETURN ISNULL(@NetPrice, 0) * 1.23END;GOFunkcja przyjmuje jeden argument o nazwie @NetPrice typu MONEY i zwraca pojedynczą wartość typu MONEY, która stanowi finalną cenę brutto.
Operacje wewnątrz bloku BEGIN ... END:
ISNULL(@NetPrice, 0)– jeśli ktoś przekaże do funkcji wartość pustą (NULL), funkcja zamieni ją na 0. Zapobiega to błędom w obliczeniach (wynikiem mnożenia NULL przez cokolwiek zawsze byłby NULL)- mnożenie przez 1.23 – funkcja mnoży wartość netto przez 1,23, co matematycznie odpowiada powiększeniu wartości netto o 23%
Funkcje skalarne w klauzuli SELECT
W zapytaniu pobieramy nazwy produktów oraz ich ceny netto. Następnie za pomocą funkcji użytkownika o nazwie dbo.CalculateGrossPrice zwracamy odpowiadające im ceny brutto.
SELECT ProductName, UnitPrice AS Netto, dbo.CalculateGrossPrice(UnitPrice) AS BruttoFROM Products;Wynik zapytania:
| ProductName | Netto | Brutto |
|---|---|---|
| Chai | 18,00 | 22,14 |
| Chang | 19,00 | 23,37 |
| Aniseed Syrup | 10,00 | 12,30 |
| Chef Anton’s Cajun Seasoning | 22,00 | 27,06 |
| Chef Anton’s Gumbo Mix | 21,35 | 26,2605 |
| Grandma’s Boysenberry Spread | 25,00 | 30,75 |
Funkcje skalarne w klauzulach WHERE oraz ORDER BY
W zapytaniu:
- filtrujemy rekordy na podstawie wyniku funkcji dbo.CalculateGrossPrice – szukamy produktów, których cena brutto przekracza 50
- sortujemy wyniki według wartości zwróconej przez funkcję dbo.CalculateGrossPrice
SELECT ProductName, UnitPrice AS Netto, dbo.CalculateGrossPrice(UnitPrice) AS BruttoFROM ProductsWHERE dbo.CalculateGrossPrice(UnitPrice) > 50ORDER BY dbo.CalculateGrossPrice(UnitPrice);Wynik:
| ProductName | Netto | Brutto |
|---|---|---|
| Schoggi Schokolade | 43,90 | 53,997 |
| Vegie-spread | 43,90 | 53,997 |
| Rössle Sauerkraut | 45,60 | 56,088 |
| Ipoh Coffee | 46,00 | 56,58 |
| Tarte au sucre | 49,30 | 60,639 |
Funkcje skalarne w klauzulach GROUP BY i HAVING
W przykładzie:
- grupujemy dane według wartości zwróconej przez funkcję dbo.CalculateGrossPrice(UnitPrice)
- filtrujemy grupy na podstawie wyniku funkcji – wyświetlamy tylko te grupy cenowe, gdzie cena brutto jest większa niż 30
SELECT dbo.CalculateGrossPrice(UnitPrice) AS GrossPrice, COUNT(*) AS ProductCountFROM ProductsGROUP BY dbo.CalculateGrossPrice(UnitPrice)HAVING dbo.CalculateGrossPrice(UnitPrice) > 30;Wynik:
| GrossPrice | ProductCount |
|---|---|
| 41,82 | 1 |
| 42,804 | 1 |
| 44,28 | 1 |
| 46,74 | 2 |
| 47,97 | 1 |
| 49,20 | 1 |
| 53,997 | 2 |
Funkcje skalarne w instrukcji CASE
W przykładzie:
- używamy funkcji dbo.CalculateGrossPrice(UnitPrice), aby najpierw obliczyć cenę z podatkiem
- tworzymy kategorie produktów „Tani”, „Standard”, „Premium” na podstawie ceny brutto
SELECT ProductName, UnitPrice AS Netto, dbo.CalculateGrossPrice(UnitPrice) AS Brutto, CASE WHEN dbo.CalculateGrossPrice(UnitPrice) < 20 THEN 'Tani' WHEN dbo.CalculateGrossPrice(UnitPrice) <=100 THEN 'Standard' WHEN dbo.CalculateGrossPrice(UnitPrice) > 100 THEN 'Premium' ELSE 'Do wyceny' END AS SegmentCenowyFROM Products;Wynik:
| ProductName | Netto | Brutto | SegmentCenowy |
|---|---|---|---|
| Chai | 18,00 | 22,14 | Standard |
| Chang | 19,00 | 23,37 | Standard |
| Aniseed Syrup | 10,00 | 12,30 | Tani |
| Chef Anton’s Cajun Seasoning | 22,00 | 27,06 | Standard |
| Chef Anton’s Gumbo Mix | 21,35 | 26,2605 | Standard |
| Grandma’s Boysenberry Spread | 25,00 | 30,75 | Standard |
| Uncle Bob’s Organic Dried Pears | 30,00 | 36,90 | Standard |
| Northwoods Cranberry Sauce | 40,00 | 49,20 | Standard |
| Mishi Kobe Niku | 97,00 | 119,31 | Premium |
W powyższym przykładzie funkcja dbo.fn_CalculateGrossPrice zostanie wykonana wielokrotnie dla każdego wiersza – raz w kolumnie Brutto i po jednym razie dla każdego warunku WHEN, w którym występuje. Może to wpłynąć negatywnie na wydajność zapytania.
Aby zoptymalizować zapytanie i uniknąć wielokrotnego wywoływania tej samej funkcji dla jednego wiersza, możemy użyć operatora CROSS APPLY
SELECT p.ProductName, p.UnitPrice AS Netto, ca.Brutto, CASE WHEN ca.Brutto < 20 THEN 'Tani' WHEN ca.Brutto <= 100 THEN 'Standard' WHEN ca.Brutto > 100 THEN 'Premium' ELSE 'Do wyceny' END AS SegmentCenowyFROM Products pCROSS APPLY ( SELECT dbo.CalculateGrossPrice(p.UnitPrice) AS Brutto) ca;Modyfikowanie i usuwanie własnych funkcji skalarnych
Aby zmodyfikować skalarną funkcję użytkownika możemy użyć polecenia ALTER FUNCTION
W przykładzie zmieniamy stawkę podatku z 23% na 8% oraz dodajemy zaokrąglanie wyniku do dwóch miejsc po przecinku.
ALTER FUNCTION dbo.CalculateGrossPrice( @NetPrice MONEY)RETURNS MONEYASBEGIN -- Cena powiększona o 8% VAT i zaokrąglona do 2 miejsc po przecinku RETURN ROUND(ISNULL(@NetPrice, 0) * 1.08, 2)END;GOAby usunąć funkcję używamy polecenia DROP FUNCTION
Jeśli funkcja nie jest już wykorzystywana przez inne obiekty bazy danych, można ją usunąć
DROP FUNCTION dbo.CalculateGrossPrice;GOWyzwania wydajnościowe funkcji skalarnych
Tworzenie funkcji skalarnych (UDF) w SQL Server umożliwia porządkowanie logiki biznesowej kodu i poprawę czytelności zapytań. Używanie skalarnych funkcji może jednak wiązać się z problemami wydajnościowymi gdyż:
- Funkcje skalarne są wykonywane w trybie wiersz po wierszu, co powoduje wielokrotne wywołania tej samej logiki i znacząco wydłuża czas wykonania zapytań
- Logika zawarta w skalarnej UDF jest w dużej mierze niewidoczna dla optymalizatora zapytań, co ogranicza jego zdolność do tworzenia efektywnych planów wykonania
- Zapytania korzystające ze skalarnych UDF często nie mogą być wykonywane równolegle, co prowadzi do braku możliwości wykorzystania dostępnych zasobów sprzętowych
- Zastosowanie funkcji skalarnej w klauzulach
SELECT,WHERElubJOINmoże uniemożliwić efektywne użycie indeksów - Koszt wykonania skalarnej UDF nie jest jednoznacznie widoczny w planach zapytań, co utrudnia analizę i diagnozowanie problemów wydajnościowych
W wielu przypadkach lepszym rozwiązaniem może być użycie wyrażeń inline lub funkcji tabelarycznych inline.
Tworzenie funkcji skalarnych (UDF) w SQL Server to ważny element pracy z bazami danych. W praktyce warto łączyć je ze świadomym podejściem do wydajności i analizy planów wykonania zapytań.
Jeśli chcesz pójść dalej w pracy z SQL Server i przećwiczyć omawiane zagadnienia w uporządkowanej formie, możesz skorzystać z kursu SQL Server – poziom zaawansowany, na którym prezentujemy funkcje użytkownika w szerszym kontekście.
