スカラーユーザー定義関数の作成(プレビュー)

適用対象:✅ Microsoft Fabric の SQL 分析エンドポイントおよびウェアハウス

Microsoft Fabric Data Warehouse分散クエリの一部としてスカラーユーザー定義関数(UDF)を実行するために2つのインライン技術を使用しています。

注

スカラー UDF は現在、Fabric Data Warehouse のプレビュー機能です。

この記事では、非線列UDFを線形関数に変換する方法や非線列UDFの作成方法を学びます。

非線化可能なスカラーUDFを線形にしてください

スカラーUDFを線列にするために必要なT-SQLコードの変更は、定義を非線形にする構成文によって異なります。

以下の例は、データアクセスUDFにおいて複数の RETURN 文、 @@ROWCOUNT、時間依存関数の書き換え方を示しています。

複数のRETURN文を置き換える

以下の関数はテーブルから顧客型を読み取り、複数の RETURN 文を含みます。 関数がテーブルデータにアクセスするため、スカラーUDFインライン化要件が適用されます。 複数の RETURN 文により、この定義は非線列的になります。

関数を書き直す準備をしながら、関数を作成する WITH INLINE = AUTO を指定します:

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

関数がインライン可能かどうか確認してください

sys.sql_modulesカタログ ビューは、スカラー UDF のインライン化可能性を記述する 2 つのプロパティを提供します。

  • is_inlineable 関数定義が直列可能かどうかを示します。 1 の値はインライン化可能を意味し、0 はインライン化不可を意味します。
  • inline_eligibility_mask どのインライン技術が関数定義を処理できるかを特定します。 値の詳細については、「 スカラーUDFがインライン化できるかどうか確認」をご覧ください。

以下のクエリを使って 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

メタデータは関数の定義を記述します。 dbo.GetDiscountの初期定義では、is_inlineableが0です。

関数を書き直して結果をローカル変数に割り当て、最後に1つの 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

改訂された関数は1つの RETURN 文を持ち、スカラーUDFインライン要件を満たします。 メタデータクエリを再度実行して、1 が is_inlineable であることを確認してください。

注

Expression Blockのインライン化は複数の RETURN 文をサポートしています。 この例はテーブルデータを読み取るため、スカラーUDFインラインングを用いており、これは単一の RETURN 文を必要とします。

@@ROWCOUNTを初期値に置き換えます

以下のデータアクセスUDFは、クエリが顧客を見つけたかどうかを判断するために @@ROWCOUNT を使用します。 @@ROWCOUNT はスカラーUDFインラインでサポートされていないため、定義は非インラインです:

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

一致する行がない場合の結果に変数を初期化します。 SELECT文は行が見つかったときのみ値を変更するため、@@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

前の例のメタデータクエリを関数名にして CustomerExists を実行します。 初期の定義は is_inlineable = 0、改訂定義は is_inlineable = 1です。

時間依存の値をデータアクセスUDFに渡す

以下の関数はテーブルアクセスと GETUTCDATE()を組み合わせています。 テーブルアクセスにはスカラーUDFインラインングが必要であり、UDFボディ内の時間依存関数により定義が非インライン化されます。

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

代わりに現在の日付と時刻をパラメータとして関数に入力してください:

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

呼び出しクエリの GETUTCDATE() を評価し、その値をUDFに渡します:

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

クエリをインラインに設定する

このセクションのクエリ形状要件は、クエリ内の少なくとも1つのUDFがスカラーUDFインラインに依存している場合に適用されます。 クエリ内のすべてのUDFがExpression Blockインラインで処理されている場合、これらのクエリ形状はここで示す書き換えなしで動作します。

以下の例は、前節のインライン可能なデータアクセス版 dbo.GetDiscount を使用しています。

共通テーブル式を置き換える

共通テーブル式(CTE)を使うクエリは、スカラーUDFインラインに依存する関数をインライン化できません。 以下のクエリはサポートされていません:

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

結果を一時テーブルにマシライズし、その一時テーブルにクエリを行います:

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

UDFコールをGROUP BYから移動させる

以下のクエリは GROUP BY 節で直接UDFを呼び出しますが、スカラーUDFインラインに依存する関数にはサポートされていません。

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

UDFの結果を派生テーブルで計算し、その結果を外部クエリでグループ化します:

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

ORDER BY 句で列エイリアスを使用する

スカラー UDF のインライン化に依存する UDF を ORDER BY 句で直接呼び出さないでください。 結果を一度Projectし、列別名または列の順序数で並べます:

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

非線列スカラーUDFを作成する

定義が直列化できない場合でも、独立した計算用の関数が必要になるかもしれません。 例えば、アプリケーションやレポートがUDFを一度呼び出してビジネス指標を計算することがあります。

次の関数は顧客テーブルから年初来の収益を読み取り、 WHILE ループを使って残りの月の平均月間収益を加算します。 関数はテーブルデータにアクセスするため、スカラーUDFインラインに依存しており、 WHILEをサポートしていません。 したがって、関数の定義は非線列的である。

WITH INLINE = AUTOを使用すると、定義がインライン化可能でなくても関数を作成できるようになります:

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

ダッシュボードの指標を一度だけ計算するために、スタンドアロンの呼び出しを使ってください:

DECLARE @ProjectedAnnualRevenue DECIMAL(18, 2);

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

SELECT @ProjectedAnnualRevenue AS ProjectedAnnualRevenue;
GO

クエリは、現在のUTC月までの平均月間収入に基づく年間収益の予測値を返します。

INLINE = AUTO 関数の作成は可能ですが、非インライン関数が分散クエリで使用可能になるわけではありません。 例えば、 dbo.GetProjectedAnnualRevenue は SELECT ... FROM dbo.Customer クエリでは使えません。