De zoekwoordterm hier is INLINE TABEL GEWAARDEERDE FUNCTIES . U hebt twee soorten T-SQL-tabelwaardige functies:multi-statement en inline. Als je T-SQL-functie begint met een BEGIN-instructie, dan wordt het onzin - scalair of anderszins. U kunt geen tijdelijke tabel in een inline krijgen functie met tabelwaarde, dus ik neem aan dat je van scalaire functie naar tabelwaardefunctie met meerdere instructies bent gegaan, wat waarschijnlijk erger zal zijn.
Uw inline tabel gewaardeerde functie (iTVF) zou er ongeveer zo uit moeten zien:
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;
Merk op dat, in de code die je hebt gepost, je DATEDIFF
statement mist het datepart
parameter. Als het er ongeveer zo uit zou moeten zien:
@x int = DATEDIFF(DAY, GETDATE(),'1/1/2000')
Een beetje verder gaan - het is belangrijk om te begrijpen waarom iTVF's beter zijn dan T-SQL scalair gewaardeerde, door de gebruiker gedefinieerde functies. Het is niet omdat functies met tabelwaarde sneller zijn dan functies met schaalwaarde, maar omdat Microsoft's implementatie van T-SQL inline-functies sneller is dan hun implementatie van T-SQL-functies die niet inline zijn. Let op de volgende drie functies die hetzelfde doen:
-- 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
Nu voor enkele voorbeeldgegevens en prestatietests:
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
Resultaten:
scalar
------------------------------------------------------------
2786
mTVF
------------------------------------------------------------
41536
iTVF
------------------------------------------------------------
153
De scalaire udf liep gedurende 2,7 seconden, 41 seconden voor de mtvf en 0,153 seconden voor de iTVF. Laten we eens kijken naar de geschatte uitvoeringsplannen om te begrijpen waarom:
Je ziet dit niet als je naar het daadwerkelijke uitvoeringsplan kijkt, maar met de scalaire udf en mtvf roept de optimizer een slecht uitgevoerde subroutine aan voor elke rij; de iTVF niet. Citaat van Paul White's loopbaanverandering artikel over TOEPASSEN Paul schrijft:
Met andere woorden, iTVF's stellen de optimizer in staat om de query te optimaliseren op manieren die gewoon niet mogelijk zijn wanneer al die andere code moet worden uitgevoerd. Een van de vele andere voorbeelden van waarom iTVF's superieur zijn, is dat ze de enige van de drie bovengenoemde functietypes zijn die parallellisme mogelijk maken. Laten we elke functie nog een keer uitvoeren, deze keer met het werkelijke uitvoeringsplan ingeschakeld en met traceflag 8649 (die een parallel uitvoeringsplan afdwingt):
-- 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);
Uitvoeringsplannen:
Die pijlen die je ziet voor het uitvoeringsplan van de iTVF is parallellisme - al je CPU's (of zoveel als de MAXDOP
van je SQL-instantie instellingen toestaan) samenwerken. T-SQL scalaire en mtvf UDF's kunnen dat niet. Wanneer Microsoft inline scalaire UDF's introduceert, zou ik die voorstellen voor wat u doet, maar tot die tijd:als prestaties zijn wat u zoekt, dan is inline de enige manier om te gaan en daarvoor zijn iTVF's de enige game in de stad.
Merk op dat ik voortdurend de nadruk legde op T-SQL als we het over functies hebben... CLR Scalaire en tabelwaardefuncties kunnen prima zijn, maar dat is een ander onderwerp.