programowanieBackend developer

Jak zrealizować skomplikowane zasady biznesowe i obliczenia bezpośrednio w SQL za pomocą funkcji zdefiniowanych przez użytkownika (User Defined Functions, UDF)? Jakie są różnice i ograniczenia pomiędzy skalarnymi a tabelarycznymi UDF, kiedy jest to zasadnę, a kiedy lepiej zrealizować logikę w aplikacji zewnętrznej?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

W SQL można realizować logikę biznesową za pomocą funkcji zdefiniowanych przez użytkownika (User Defined Functions, UDF), co pozwala przenieść część obliczeń i zasad biznesowych do bazy danych.

Funkcje skalarne zwracają jedną wartość i są wywoływane w wyrażeniach — na przykład, aby obliczyć całkowitą kwotę na podstawie parametrów:

CREATE FUNCTION dbo.GetDiscount(@price DECIMAL(10,2), @loyaltyLevel INT) RETURNS DECIMAL(10,2) AS BEGIN RETURN @price * CASE WHEN @loyaltyLevel = 1 THEN 0.95 WHEN @loyaltyLevel = 2 THEN 0.90 ELSE 1.0 END END; -- Przykład użycia: SELECT Name, dbo.GetDiscount(Price, LoyaltyLevel) AS DiscountPrice FROM Products;

Funkcje tabelaryczne zwracają zestaw wierszy (tabelę):

CREATE FUNCTION dbo.ActiveOrders(@userId INT) RETURNS TABLE AS RETURN ( SELECT * FROM Orders WHERE UserId = @userId AND Status = 'Active' ); -- Użycie: SELECT * FROM dbo.ActiveOrders(123);

Różnice, ograniczenia i zalecenia:

  • Skalarne UDF często mają niską wydajność w dużych zbiorach danych, ponieważ są wywoływane wierszami;
  • Tabela UDF lepiej integrują się w plany zapytań i pozwalają używać ich jak zwykłych tabel;
  • Logikę, która wymaga dostępu do zewnętrznych zasobów lub skomplikowanej obróbki biznesowej, lepiej przenieść poza SQL.

Pytanie z podstępem.

Jaka jest różnica pomiędzy funkcją tabelaryczną INLINE a funkcją tabelaryczną wielooperacyjną (multi-statement)? Jak ten wybór wpływa na wydajność?

Odpowiedź i przykład:

  • Funkcja INLINE (single-statement, zwraca od razu SELECT) jest optymalizowana jako część głównego zapytania, nie tworzy dodatkowej tabeli i działa szybciej, plan zapytania jest wspólny.
  • Funkcja wielooperacyjna tworzy tabelę tymczasową (zmienną tabelaryczną), co zazwyczaj pogarsza plan zapytania, mogą wystąpić błędy oceny kardynalności i spadek wydajności.
-- INLINE CREATE FUNCTION dbo.InlineActiveOrders(@userId INT) RETURNS TABLE AS RETURN ( SELECT * FROM Orders WHERE UserId = @userId AND Status = 'Active' ); -- Funkcja wielooperacyjna CREATE FUNCTION dbo.MultiActiveOrders(@userId INT) RETURNS @result TABLE (...) AS BEGIN INSERT INTO @result SELECT ... -- logika RETURN END

Historia

Projekt: Analiza finansowa. Używaliśmy skalarnych UDF w części select raportu na dużej masie tabel: SELECT Amount, dbo.CalcTax(Amount, Type) FROM Transactions. Zapytanie trwało 5-10 minut z powodu przetwarzania UDF wierszami. Przepisano na wbudowane CASE: czas został skrócony do sekund.


Historia

Projekt: E-commerce. Do wyszukiwania koszyków użytkowników używaliśmy wielooperacyjnych funkcji tabelarycznych, gdzie była logika filtrowania z kilku etapów. Okazało się, że SQL nie buduje optymalnego planu i wykonuje skanowanie, nawet jeśli potrzebny jest jeden element. Zastąpienie funkcją INLINE od razu przyspieszyło zapytania 50-krotnie.


Historia

Projekt: CRM. Przenieśliśmy logikę biznesową przyznawania bonusów do skomplikowanej UDF. Po kilku miesiącach zrozumieliśmy, że formuły biznesowe się zmieniły, a aktualizacja funkcji jest trudna ze względu na sztywne powiązania z danymi. W iteracyjnym produkcie UDF doprowadziło do dużych wydatków na koordynację zmian między backendem a bazą danych.