Crea una funzione scalare definita dall'utente (anteprima)

Si applica a:✅ endpoint di analisi SQL e magazzino dati in Microsoft Fabric

Microsoft Fabric Data Warehouse utilizza due tecniche di inlining per eseguire funzioni scalari definite dall'utente (UDF) come parte delle query distribuite:

  • L'inlining ExprBlock supporta UDF scalari basati sul calcolo che non fanno riferimento ai dati nei loro corpi di funzione e supporta una vasta gamma di forme di calling-query.
  • L'inlining scalare UDF in Fabric Data Warehouse (anteprima) ha requisiti rigorosi sia per la definizione della funzione sia per la query di chiamata. Per maggiori informazioni sulle due tecniche e sui loro requisiti, vedi CREATE FUNCTION.

Annotazioni

Gli UDF scalari sono attualmente una funzione di anteprima in Fabric Data Warehouse.

In questo articolo, scopri come convertire una UDF non inlineabile in una funzione inlineabile o creare una UDF non inlineabile.

Rendi una UDF scalare non inlineabile

Le modifiche al codice T-SQL necessarie per rendere una UDF scalare inlineabile dipendono da quale costrutto rende la sua definizione non inlineabile.

I seguenti esempi mostrano come riscrivere istruzioni multiple RETURN, @@ROWCOUNT e una funzione dipendente dal tempo in un UDF di accesso ai dati.

Sostituire più istruzioni RETURN

La seguente funzione legge il tipo cliente da una tabella e contiene molteplici RETURN istruzioni. Poiché la funzione accede ai dati delle tabelle, si applicano i requisiti scalari di inlining UDF. Molteplici RETURN affermazioni rendono questa definizione non inlineabile.

Specifica WITH INLINE = AUTO per creare la funzione mentre ti prepari a riscriverla:

CREATE OR ALTER FUNCTION dbo.GetDiscount (@CustomerID INT)
RETURNS DECIMAL(5, 2)
WITH INLINE = AUTO
AS
BEGIN
    DECLARE @CustomerType VARCHAR(20);

    SELECT @CustomerType = CustomerType
    FROM dbo.Customer
    WHERE CustomerID = @CustomerID;

    IF @CustomerType = 'Regular' RETURN 0.05;
    IF @CustomerType = 'Premium' RETURN 0.10;
    IF @CustomerType = 'VIP' RETURN 0.15;
    RETURN 0.01;
END;
GO

Controlla se la funzione è inlineabile

La sys.sql_modules vista del catalogo espone due proprietà che descrivono l'inlineabilità scalare della UDF:

  • is_inlineable indica se la definizione della funzione è inlineabile. Un valore di 1 significa inlineabile, e 0 significa non inlineabile.
  • inline_eligibility_mask identifica quale tecnica di inlining può elaborare la definizione della funzione. Per maggiori informazioni sui valori, vedi Verifica se un UDF scalare può essere inlineato.

Usa la seguente query per ispezionare dbo.GetDiscount:

SELECT
    SCHEMA_NAME(o.schema_id) AS function_schema,
    o.name AS function_name,
    m.is_inlineable,
    m.inline_eligibility_mask
FROM sys.sql_modules AS m
INNER JOIN sys.objects AS o
    ON m.object_id = o.object_id
WHERE o.type = 'FN'
    AND SCHEMA_NAME(o.schema_id) = 'dbo'
    AND o.name = 'GetDiscount';
GO

I metadati descrivono la definizione della funzione. Per la definizione iniziale di dbo.GetDiscount, is_inlineable è 0.

Riscrivi la funzione per assegnare il risultato a una variabile locale e utilizza un'unica istruzione finale RETURN:

CREATE OR ALTER FUNCTION dbo.GetDiscount (@CustomerID INT)
RETURNS DECIMAL(5, 2)
AS
BEGIN
    DECLARE @Discount DECIMAL(5, 2) = 0.01;

    SELECT @Discount =
        CASE CustomerType
            WHEN 'Regular' THEN 0.05
            WHEN 'Premium' THEN 0.10
            WHEN 'VIP' THEN 0.15
            ELSE 0.01
        END
    FROM dbo.Customer
    WHERE CustomerID = @CustomerID;

    RETURN @Discount;
END;
GO

La funzione rivista ha una sola RETURN affermazione e soddisfa i requisiti scalari di inlining UDF. Esegui di nuovo la query dei metadati per verificare che is_inlineable sia 1.

Annotazioni

L'inline di ExprBlock supporta più istruzioni RETURN. Questo esempio legge i dati delle tabelle e quindi utilizza l'inlining scalare UDF, che richiede una singola RETURN istruzione.

Sostituisci @@ROWCOUNT con un valore iniziale

La seguente UDF di accesso ai dati utilizza @@ROWCOUNT per determinare se la query ha trovato un cliente. @@ROWCOUNT non è supportata dall'inlining scalare UDF, quindi la definizione non è inlineabile:

CREATE OR ALTER FUNCTION dbo.CustomerExists (@CustomerID INT)
RETURNS BIT
WITH INLINE = AUTO
AS
BEGIN
    DECLARE @CustomerExists BIT = 1;

    SELECT @CustomerExists = 1
    FROM dbo.Customer
    WHERE CustomerID = @CustomerID;

    IF @@ROWCOUNT = 0 SET @CustomerExists = 0;

    RETURN @CustomerExists;
END;
GO

Inizializzare la variabile con il risultato nel caso in cui non vi sia alcuna riga corrispondente. Cambia SELECT il valore solo quando si trova una riga, eliminando così la necessità di ispezionare @@ROWCOUNT:

CREATE OR ALTER FUNCTION dbo.CustomerExists (@CustomerID INT)
RETURNS BIT
AS
BEGIN
    DECLARE @CustomerExists BIT = 0;

    SELECT @CustomerExists = 1
    FROM dbo.Customer
    WHERE CustomerID = @CustomerID;

    RETURN @CustomerExists;
END;
GO

Esegui la query dei metadati dell'esempio precedente con CustomerExists come nome della funzione. La definizione iniziale ha is_inlineable = 0, e la definizione rivista ha is_inlineable = 1.

Passa un valore dipendente dal tempo in un UDF di accesso ai dati

La seguente funzione combina l'accesso alla tabella con GETUTCDATE(). L'accesso alla tabella richiede l'inlining scalare UDF, dove una funzione dipendente dal tempo nel corpo UDF rende la definizione non inlineabile:

CREATE OR ALTER FUNCTION dbo.GetCustomerTenureDays (@CustomerID INT)
RETURNS INT
WITH INLINE = AUTO
AS
BEGIN
    DECLARE @MemberSince DATE;

    SELECT @MemberSince = MemberSince
    FROM dbo.Customer
    WHERE CustomerID = @CustomerID;

    RETURN DATEDIFF(DAY, @MemberSince, GETUTCDATE());
END;
GO

Passa invece la data e l'ora attuali nella funzione come parametro:

CREATE OR ALTER FUNCTION dbo.GetCustomerTenureDays
(
    @CustomerID INT,
    @AsOfDate DATETIME2(6)
)
RETURNS INT
AS
BEGIN
    DECLARE @MemberSince DATE;

    SELECT @MemberSince = MemberSince
    FROM dbo.Customer
    WHERE CustomerID = @CustomerID;

    RETURN DATEDIFF(DAY, @MemberSince, @AsOfDate);
END;
GO

Valuta GETUTCDATE() nella query chiamante e passa il suo valore all'UDF:

SELECT
    CustomerID,
    CustomerName,
    dbo.GetCustomerTenureDays(CustomerID, GETUTCDATE()) AS TenureDays
FROM dbo.Customer
ORDER BY CustomerID;
GO

Rendi una query inlineabile

I requisiti relativi alla struttura della query descritti in questa sezione si applicano quando almeno una UDF in una query si basa sull'inline delle UDF scalari. Quando ogni UDF in una query viene gestito tramite ExprBlock inlining, queste forme di query funzionano senza le riscritture mostrate qui.

I seguenti esempi utilizzano la versione dbo.GetDiscount inlineabile e di accesso ai dati della sezione precedente.

Sostituire un'espressione comune in una tabella

Una query che utilizza un'espressione di tabella comune (CTE) non può inlineare una funzione che dipende dall'inlining scalare UDF. La seguente query non è supportata:

WITH CustomerDiscounts AS
(
    SELECT
        CustomerID,
        CustomerName,
        dbo.GetDiscount(CustomerID) AS DiscountRate
    FROM dbo.Customer
)
SELECT CustomerID, CustomerName, DiscountRate
FROM CustomerDiscounts;
GO

Materializza il risultato in una tabella temporanea, e poi consulta la tabella temporanea:

DROP TABLE IF EXISTS #CustomerDiscounts;
GO

CREATE TABLE #CustomerDiscounts
(
    CustomerID INT NOT NULL,
    CustomerName VARCHAR(100) NOT NULL,
    DiscountRate DECIMAL(5, 2) NOT NULL
)
WITH (DISTRIBUTION = ROUND_ROBIN);
GO

INSERT INTO #CustomerDiscounts
(
    CustomerID,
    CustomerName,
    DiscountRate
)
SELECT
    CustomerID,
    CustomerName,
    dbo.GetDiscount(CustomerID)
FROM dbo.Customer;
GO

SELECT CustomerID, CustomerName, DiscountRate
FROM #CustomerDiscounts;
GO

Sposta una chiamata UDF al di fuori di GROUP BY

La seguente query chiama direttamente l'UDF nella GROUP BY clausola ed è non supportata per una funzione che dipende dall'inlining scalare UDF:

SELECT
    dbo.GetDiscount(CustomerID) AS DiscountRate,
    COUNT(*) AS CustomerCount
FROM dbo.Customer
GROUP BY dbo.GetDiscount(CustomerID);
GO

Calcola il risultato UDF in una tabella derivata e poi raggruppa il risultato nella query esterna:

SELECT
    DiscountRate,
    COUNT(*) AS CustomerCount
FROM
(
    SELECT dbo.GetDiscount(CustomerID) AS DiscountRate
    FROM dbo.Customer
) AS CustomerDiscounts
GROUP BY DiscountRate;
GO

Usa un alias di colonna in ORDER BY

Evita di chiamare un UDF che dipende direttamente dall'inlining scalare dell'UDF ORDER BY nella clausola. Proietta il risultato una sola volta e ordinalo in base all'alias della colonna o al numero ordinale della colonna:

SELECT
    CustomerID,
    CustomerName,
    dbo.GetDiscount(CustomerID) AS DiscountRate
FROM dbo.Customer
ORDER BY DiscountRate;
GO

Creare un UDF scalare non inlineabile

Potresti aver bisogno di una funzione per un calcolo autonomo anche quando la sua definizione non può essere inlineata. Ad esempio, un'applicazione o un report potrebbe chiamare una UDF una volta per calcolare una metrica aziendale.

La seguente funzione legge il fatturato da inizio anno dalla tabella clienti, poi utilizza un WHILE ciclo per aggiungere il fatturato medio mensile per ogni mese rimanente. Poiché la funzione accede ai dati delle tabelle, si basa sull'inlining scalare UDF, che non supporta WHILE. La definizione della funzione è quindi non inlineabile.

Usa WITH INLINE = AUTO per permettere la creazione della funzione anche se la sua definizione non è inlineabile:

CREATE OR ALTER FUNCTION dbo.GetProjectedAnnualRevenue (@MonthsElapsed INT)
RETURNS DECIMAL(18, 2)
WITH INLINE = AUTO
AS
BEGIN
    DECLARE @YearToDateRevenue DECIMAL(38, 6);
    DECLARE @ProjectedRevenue DECIMAL(38, 6);
    DECLARE @MonthNumber INT = @MonthsElapsed + 1;

    IF @MonthsElapsed BETWEEN 1 AND 12
    BEGIN
        SELECT @YearToDateRevenue = SUM(YearToDateRevenue)
        FROM dbo.Customer;

        SET @ProjectedRevenue = @YearToDateRevenue;

        WHILE @MonthNumber <= 12
        BEGIN
            SET @ProjectedRevenue =
                @ProjectedRevenue + (@YearToDateRevenue / @MonthsElapsed);
            SET @MonthNumber = @MonthNumber + 1;
        END;
    END;

    RETURN @ProjectedRevenue;
END;
GO

Usa una chiamata standalone per calcolare la metrica una volta per la dashboard:

DECLARE @ProjectedAnnualRevenue DECIMAL(18, 2);

SET @ProjectedAnnualRevenue =
    dbo.GetProjectedAnnualRevenue(MONTH(GETUTCDATE()));

SELECT @ProjectedAnnualRevenue AS ProjectedAnnualRevenue;
GO

La query restituisce ricavi annuali previsti in base al fatturato medio mensile fino al mese UTC corrente.

INLINE = AUTO permette di creare la funzione, ma non rende una funzione non inlineabile idonea all'uso in una query distribuita. Ad esempio, dbo.GetProjectedAnnualRevenue non può essere usato in una SELECT ... FROM dbo.Customer query.