Ключовият термин тук е INLINE ФУНКЦИИ С ТАБЛИЧНА СТОЙНОСТ . Имате два типа T-SQL таблични стойностни функции:мулти-изрази и вградени. Ако вашата T-SQL функция започва с оператор BEGIN, тогава това ще бъде глупост - скаларна или друга. Не можете да поставите временна таблица във вградена функция с таблични стойности, така че предполагам, че сте преминали от скаларна към функция с таблични стойности с множество изрази, което вероятно ще бъде по-лошо.
Вашата вградена таблично стойностна функция (iTVF) трябва да изглежда по следния начин:
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;
Имайте предвид, че в публикувания от вас код вашият DATEDIFF
в оператора липсва datepart
параметър. Ако трябва да изглежда нещо като:
@x int = DATEDIFF(DAY, GETDATE(),'1/1/2000')
Продължавайки малко по-далеч - важно е да разберем защо iTVF са по-добри от дефинираните от потребителя функции със скаларна стойност на T-SQL. Не защото функциите с таблични стойности са по-бързи от функциите със скаларни стойности, а защото внедряването на T-SQL вградени функции на Microsoft е по-бързо от имплементирането на T-SQL функции, които не са вградени. Обърнете внимание на следните три функции, които правят едно и също нещо:
-- 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
Сега за примерни данни и тест за ефективност:
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
Резултати:
scalar
------------------------------------------------------------
2786
mTVF
------------------------------------------------------------
41536
iTVF
------------------------------------------------------------
153
Скаларният udf се изпълнява за 2,7 секунди, 41 секунди за mtvf и 0,153 секунди за iTVF. За да разберем защо, нека да разгледаме прогнозните планове за изпълнение:
Не виждате това, когато гледате действителния план за изпълнение, но със скаларните udf и mtvf, оптимизаторът извиква някаква лошо изпълнена подпрограма за всеки ред; iTVF не го прави. Цитирайки промяната в кариерата на Пол Уайт статия за APPLY Павел пише:
С други думи, iTVF дава възможност на оптимизатора да оптимизира заявката по начини, които просто не са възможни, когато целият този друг код трябва да бъде изпълнен. Един от многото други примери защо iTVF са по-добри е, че те са единственият от трите гореспоменати типа функции, които позволяват паралелизъм. Нека изпълним всяка функция още веднъж, този път с включен действителен план за изпълнение и с traceflag 8649 (който принуждава паралелен план за изпълнение):
-- 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);
Планове за изпълнение:
Тези стрелки, които виждате за плана за изпълнение на iTVF, са паралелизъм - всичките ви CPU (или толкова, колкото MAXDOP
на вашия SQL екземпляр настройките позволяват) работят заедно. T-SQL скаларните и mtvf UDF не могат да направят това. Когато Microsoft въведе вградени скаларни UDF, тогава бих предложил тези за това, което правите, но дотогава:ако производителността е това, което търсите, тогава вградените са единственият начин и за това iTVF са единствената игра в града.
Имайте предвид, че непрекъснато наблягах на T-SQL когато говорим за функции... CLR Scalar и таблично стойностните функции могат да бъдат добре, но това е друга тема.