Klíčové slovo je zde INLINE FUNKCE HODNOTY TABULKY . Máte dva typy hodnotných funkcí v T-SQL:vícepříkazové a inline. Pokud vaše funkce T-SQL začíná příkazem BEGIN, pak to bude svinstvo – skalární nebo jiné. Do inline nelze vložit dočasnou tabulku funkce s hodnotou tabulky, takže předpokládám, že jste přešli ze skalární na funkci s hodnotou tabulky s více příkazy, která bude pravděpodobně horší.
Vaše funkce s hodnotou inline tabulky (iTVF) by měla vypadat nějak takto:
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;
Všimněte si, že v kódu, který jste zveřejnili, je vaše DATEDIFF
v příkazu chybí datepart
parametr. Pokud by mělo vypadat něco jako:
@x int = DATEDIFF(DAY, GETDATE(),'1/1/2000')
Jdeme trochu dále – je důležité pochopit, proč jsou iTVF lepší než skalárně oceňované uživatelsky definované funkce T-SQL. Není to proto, že funkce s hodnotou tabulky jsou rychlejší než funkce s hodnotou skalární, je to proto, že implementace inline funkcí T-SQL společnosti Microsoft je rychlejší než implementace funkcí T-SQL, které nejsou inline. Všimněte si následujících tří funkcí, které dělají totéž:
-- 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
Nyní několik ukázkových dat a testu výkonu:
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
Výsledky:
scalar
------------------------------------------------------------
2786
mTVF
------------------------------------------------------------
41536
iTVF
------------------------------------------------------------
153
Skalární udf běžel 2,7 sekundy, 41 sekund pro mtvf a 0,153 sekundy pro iTVF. Abychom pochopili, proč se podíváme na odhadované plány realizace:
Když se podíváte na skutečný plán provádění, nevidíte to, ale se skalárním udf a mtvf optimalizátor zavolá pro každý řádek nějaký špatně provedený podprogram; iTVF ne. Cituji změna kariéry Paula Whitea článek o APPLY Pavel píše:
Jinými slovy, iTVF umožňuje optimalizátoru optimalizovat dotaz způsoby, které prostě nejsou možné, když je třeba provést všechny ostatní kódy. Jedním z mnoha dalších příkladů, proč jsou iTVF lepší, je, že jsou jediným ze tří výše uvedených typů funkcí, které umožňují paralelismus. Spusťte každou funkci ještě jednou, tentokrát se zapnutým plánem Skutečného provádění a s traceflagem 8649 (který vynutí paralelní plán provádění):
-- 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);
Prováděcí plány:
Tyto šipky, které vidíte pro plán provádění iTVF, jsou paralelismus – všechny vaše CPU (nebo tolik jako MAXDOP
vaší instance SQL nastavení umožňují) spolupracovat. Skalární T-SQL a mtvf UDF to neumí. Když Microsoft představí inline skalární UDF, pak bych je navrhoval pro to, co děláte, ale do té doby:pokud je výkon to, co hledáte, pak je inline jediným způsobem, jak jít, a proto jsou iTVF jedinou hrou. ve městě.
Všimněte si, že jsem neustále zdůrazňoval T-SQL když mluvíme o funkcích... CLR skalární a tabulkové funkce mohou být v pořádku, ale to je jiné téma.