Słowo kluczowe to tutaj INLINE WARTOŚCIOWE FUNKCJE TABELI . Dostępne są dwa typy funkcji T-SQL z wartościami w tabeli:wieloinstrukcyjne i wbudowane. Jeśli twoja funkcja T-SQL zaczyna się od instrukcji BEGIN, będzie to bzdura - skalarna lub inna. Nie możesz umieścić tabeli tymczasowej w inline funkcja o wartości tabeli, więc zakładam, że przeszedłeś od skalarnej do funkcji o wartości tabeli z wieloma instrukcjami, która prawdopodobnie będzie gorsza.
Twoja funkcja z wartościami w tabeli inline (iTVF) powinna wyglądać mniej więcej tak:
CREATE FUNCTION [dbo].[Compute_value]
(
@alpha FLOAT,
@bravo FLOAT,
@charle FLOAT,
@delta FLOAT
)
RETURNS TABLE WITH SCHEMABINDING AS RETURN
SELECT newValue =
CASE WHEN @alpha IS NULL OR @alpha = 0 OR @delta IS NULL OR @delta = 0 THEN 0
WHEN @bravo IS NULL OR @bravo <= 0 THEN 100
ELSE @alpha * POWER((100 / @delta),
(-2 * POWER(@charle * @bravo, DATEDIFF(<unit of measurement>,GETDATE(),'1/1/2000')/365)))
END
GO;
Zwróć uwagę, że w opublikowanym kodzie Twój DATEDIFF
w instrukcji brakuje datepart
parametr. Jeśli powinno wyglądać mniej więcej tak:
@x int = DATEDIFF(DAY, GETDATE(),'1/1/2000')
Idąc trochę dalej - ważne jest, aby zrozumieć, dlaczego iTVF są lepsze niż funkcje zdefiniowane przez użytkownika o wartości skalarnej T-SQL. Nie jest tak dlatego, że funkcje o wartościach tabelarycznych są szybsze niż funkcje o wartościach skalarnych, ale dlatego, że implementacja przez Microsoft funkcji wbudowanych T-SQL jest szybsza niż ich implementacja funkcji T-SQL, które nie są wbudowane. Zwróć uwagę na następujące trzy funkcje, które robią to samo:
-- Scalar version
CREATE FUNCTION dbo.Compute_value_scalar
(
@alpha FLOAT,
@bravo FLOAT,
@charle FLOAT,
@delta FLOAT
)
RETURNS FLOAT
AS
BEGIN
IF @alpha IS NULL OR @alpha = 0 OR @delta IS NULL OR @delta = 0
RETURN 0
IF @bravo IS NULL OR @bravo <= 0
RETURN 100
IF (@charle + @delta) / @bravo <= 0
RETURN 100
DECLARE @x int = DATEDIFF(dd, GETDATE(),'1/1/2000')
RETURN @alpha * POWER((100 / @delta), (-2 * POWER(@charle * @bravo, @x/365)))
END
GO
-- multi-statement table valued function
CREATE FUNCTION dbo.Compute_value_mtvf
(
@alpha FLOAT,
@bravo FLOAT,
@charle FLOAT,
@delta FLOAT
)
RETURNS @sometable TABLE (newValue float) AS
BEGIN
INSERT @sometable VALUES
(
CASE WHEN @alpha IS NULL OR @alpha = 0 OR @delta IS NULL OR @delta = 0 THEN 0
WHEN @bravo IS NULL OR @bravo <= 0 THEN 100
ELSE @alpha * POWER((100 / @delta),
(-2 * POWER(@charle * @bravo, DATEDIFF(DAY,GETDATE(),'1/1/2000')/365)))
END
)
RETURN;
END
GO
-- INLINE table valued function
CREATE FUNCTION dbo.Compute_value_itvf
(
@alpha FLOAT,
@bravo FLOAT,
@charle FLOAT,
@delta FLOAT
)
RETURNS TABLE WITH SCHEMABINDING AS RETURN
SELECT newValue =
CASE WHEN @alpha IS NULL OR @alpha = 0 OR @delta IS NULL OR @delta = 0 THEN 0
WHEN @bravo IS NULL OR @bravo <= 0 THEN 100
ELSE @alpha * POWER((100 / @delta),
(-2 * POWER(@charle * @bravo, DATEDIFF(DAY,GETDATE(),'1/1/2000')/365)))
END
GO
Teraz kilka przykładowych danych i testu wydajności:
SET NOCOUNT ON;
CREATE TABLE #someTable (alpha FLOAT, bravo FLOAT, charle FLOAT, delta FLOAT);
INSERT #someTable
SELECT TOP (100000)
abs(checksum(newid())%10)+1, abs(checksum(newid())%10)+1,
abs(checksum(newid())%10)+1, abs(checksum(newid())%10)+1
FROM sys.all_columns a, sys.all_columns b;
PRINT char(10)+char(13)+'scalar'+char(10)+char(13)+replicate('-',60);
GO
DECLARE @st datetime = getdate(), @z float;
SELECT @z = dbo.Compute_value_scalar(t.alpha, t.bravo, t.charle, t.delta)
FROM #someTable t;
PRINT DATEDIFF(ms, @st, getdate());
GO
PRINT char(10)+char(13)+'mtvf'+char(10)+char(13)+replicate('-',60);
GO
DECLARE @st datetime = getdate(), @z float;
SELECT @z = f.newValue
FROM #someTable t
CROSS APPLY dbo.Compute_value_mtvf(t.alpha, t.bravo, t.charle, t.delta) f;
PRINT DATEDIFF(ms, @st, getdate());
GO
PRINT char(10)+char(13)+'itvf'+char(10)+char(13)+replicate('-',60);
GO
DECLARE @st datetime = getdate(), @z float;
SELECT @z = f.newValue
FROM #someTable t
CROSS APPLY dbo.Compute_value_itvf(t.alpha, t.bravo, t.charle, t.delta) f;
PRINT DATEDIFF(ms, @st, getdate());
GO
Wyniki:
scalar
------------------------------------------------------------
2786
mTVF
------------------------------------------------------------
41536
iTVF
------------------------------------------------------------
153
Skalarny UDF działał przez 2,7 sekundy, 41 sekund dla mtvf i 0,153 sekundy dla iTVF. Aby zrozumieć, dlaczego przyjrzyjmy się szacunkowym planom wykonania:
Nie widać tego, gdy patrzysz na rzeczywisty plan wykonania, ale w przypadku skalarnych udf i mtvf optymalizator wywołuje źle wykonany podprogram dla każdego wiersza; iTVF nie. Cytując zmianę kariery Paula White'a artykuł o APLIKUJ Paweł pisze:
Innymi słowy, iTVF umożliwia optymalizatorowi optymalizację zapytania w sposób, który po prostu nie jest możliwy, gdy cały inny kod musi zostać wykonany. Jednym z wielu innych przykładów, dlaczego iTVF są lepsze, jest to, że są jedynymi z trzech wyżej wymienionych typów funkcji, które umożliwiają równoległość. Uruchommy każdą funkcję jeszcze raz, tym razem z włączonym planem Actual Execution i z traceflag 8649 (co wymusza równoległy plan wykonania):
-- don't need so many rows for this test
TRUNCATE TABLE #sometable;
INSERT #someTable
SELECT TOP (10)
abs(checksum(newid())%10)+1, abs(checksum(newid())%10)+1,
abs(checksum(newid())%10)+1, abs(checksum(newid())%10)+1
FROM sys.all_columns a;
DECLARE @x float;
SELECT TOP (10) @x = dbo.Compute_value_scalar(t.alpha, t.bravo, t.charle, t.delta)
FROM #someTable t
ORDER BY dbo.Compute_value_scalar(t.alpha, t.bravo, t.charle, t.delta)
OPTION (QUERYTRACEON 8649);
SELECT TOP (10) @x = f.newValue
FROM #someTable t
CROSS APPLY dbo.Compute_value_mtvf(t.alpha, t.bravo, t.charle, t.delta) f
ORDER BY f.newValue
OPTION (QUERYTRACEON 8649);
SELECT @x = f.newValue
FROM #someTable t
CROSS APPLY dbo.Compute_value_itvf(t.alpha, t.bravo, t.charle, t.delta) f
ORDER BY f.newValue
OPTION (QUERYTRACEON 8649);
Plany wykonania:
Te strzałki, które widzisz dla planu wykonania iTVF, to równoległość - wszystkie twoje procesory (lub tyle, ile MAXDOP
twojej instancji SQL ustawienia pozwalają) pracować razem. UDF skalarny T-SQL i mtvf nie mogą tego zrobić. Kiedy Microsoft wprowadza wbudowane skalarne UDF, sugerowałbym je dla tego, co robisz, ale do tego czasu:jeśli wydajność jest tym, czego szukasz, to inline jest jedyną drogą, a do tego iTVF są jedyną grą w mieście.
Zauważ, że stale kładłem nacisk na T-SQL kiedy mówimy o funkcjach... Funkcje CLR Scalar i Table mogą być w porządku, ale to inny temat.