Il termine chiave qui è INLINE FUNZIONI CON VALORI TABELLA . Sono disponibili due tipi di funzioni con valori con tabelle T-SQL:multi-statement e inline. Se la tua funzione T-SQL inizia con un'istruzione BEGIN, sarà una schifezza:scalare o altro. Non puoi inserire una tabella temporanea in un inline funzione con valore di tabella, quindi suppongo che tu sia passato da scalare a funzione con valore di tabella multi-istruzione che probabilmente sarà peggiore.
La tua funzione con valori di tabella in linea (iTVF) dovrebbe assomigliare a questa:
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;
Nota che, nel codice che hai pubblicato, il tuo DATEDIFF
nell'istruzione manca il datepart
parametro. Se dovrebbe assomigliare a:
@x int = DATEDIFF(DAY, GETDATE(),'1/1/2000')
Andando un po' oltre:è importante capire perché gli iTVF sono migliori delle funzioni definite dall'utente con valore scalare T-SQL. Non è perché le funzioni con valori di tabella sono più veloci delle funzioni con valori scalari, è perché l'implementazione di Microsoft delle funzioni inline T-SQL è più veloce della loro implementazione di funzioni T-SQL che non sono inline. Nota le seguenti tre funzioni che fanno la stessa cosa:
-- 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
Ora per alcuni dati di esempio e test delle prestazioni:
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
Risultati:
scalar
------------------------------------------------------------
2786
mTVF
------------------------------------------------------------
41536
iTVF
------------------------------------------------------------
153
L'udf scalare ha funzionato per 2,7 secondi, 41 secondi per mtvf e 0,153 secondi per iTVF. Per capire perché, diamo un'occhiata ai piani di esecuzione stimati:
Non lo vedi quando guardi il piano di esecuzione effettivo ma, con lo scalare udf e mtvf, l'ottimizzatore chiama alcune subroutine mal eseguite per ogni riga; l'iTVF no. Citando il cambiamento di carriera di Paul White articolo su APPLICA Paolo scrive:
In altre parole, iTVF consente all'ottimizzatore di ottimizzare la query in modi che semplicemente non sono possibili quando è necessario eseguire tutto l'altro codice. Uno dei tanti altri esempi del motivo per cui gli iTVF sono superiori è che sono l'unico dei tre tipi di funzione sopra menzionati che consentono il parallelismo. Eseguiamo ogni funzione ancora una volta, questa volta con il piano di esecuzione effettivo attivato e con traceflag 8649 (che forza un piano di esecuzione parallela):
-- 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);
Piani di esecuzione:
Quelle frecce che vedi per il piano di esecuzione di iTVF sono il parallelismo:tutte le tue CPU (o tante quante sono MAXDOP
della tua istanza SQL impostazioni consentono) lavorando insieme. Le UDF scalari T-SQL e mtvf non possono farlo. Quando Microsoft introduce le UDF scalari inline, le suggerirei per quello che stai facendo ma, fino ad allora:se le prestazioni sono ciò che stai cercando, allora inline è l'unica strada da percorrere e, per questo, gli iTVF sono l'unico gioco in città.
Nota che ho continuamente enfatizzato T-SQL quando si parla di funzioni... CLR Scalar e le funzioni con valori di tabella possono andare bene, ma questo è un argomento diverso.