【问题标题】:Rewrite scalar UDF to table UDF or insert UDF in stored procedure将标量 UDF 重写为表 UDF 或在存储过程中插入 UDF
【发布时间】:2019-05-11 08:20:20
【问题描述】:

在我的存储过程中,我创建了一个临时表以提高性能。

对于存储过程中的实际 select 语句,使用了几个标量 UDF,并且临时表替换了它们:

INSERT INTO #BEDRAGEN
    SELECT 
        DD.ColumnA, DD.ColumnB, DD.ColumnC, 
        ISNULL(DBO.SIF_get_SalesAmount(DD.ColumnA, DD.ColumnB, DD.ColumnC), 0) AS Totaalbedrag,
    FROM 
        T_InvoiceDetailDosDet as IDD

我的问题是:我想用代码替换 dbo.SIF_get_SalesAmount 或将标量 UDF 设置为表格 UDF,如果这样可以提高性能。

这个 UDF 中有什么:

返回金额。

它读取一个文件并计算几件事,然后得出一个总数。

函数有 3 个参数输入和 Amount 输出。

一块UDF:

ALTER FUNCTION [dbo].[SIF_get_SalesAmountDosDetail] 
    (@A VARCHAR(20),
     @B VARCHAR(20),
     @C VARCHAR(20) 
    )
RETURNS NUMERIC(12,2)
AS
    DECLARE @SalesAmount NUMERIC(12,2)
    ,       @SalesUnitOfAccount TINYINT
    ,       @Unit NCHAR(5)
    ,       @SalesUnit NCHAR(5)
    ,       @TotalUnits  NUMERIC(15, 3)

    SELECT        
        @unit = p.Unit,
        @SalesUnit = p.SalesUnit,
        @SalesUnitOfAccount = dd.SalesUnitOfAccount
    FROM 
        dbo.T_table p
    WHERE
        p.ColumnA = @A AND p.ColumnB = @B AND p.ColumnC = @C

    SELECT @rc = @@ROWCOUNT

    IF @rc <> 1
    BEGIN
        SELECT @SalesAmount = 0
        RETURN @SalesAmount
    END

    IF @SalesUnit = 0
    BEGIN
        SELECT @SalesUnit = 1
    END

    -- several calculations follow based on values of @Unit etc.
    --  at the end of the UDF:
    --  last if then else calculation and then returning the Amount.
    IF @SalesUnitOfAccount = 4
    BEGIN
        SELECT @PricePerDesc = @SalesUnit
        SELECT @SalesAmount = CONVERT(numeric(12, 2), round((@CurrPrice * (@TotalSalesUnits / @SalesComputQty)) - @DiscAmount, 2))
    END

    SELECT @TotalSalesAmount = @TotalSalesAmount + ISNULL(@SalesAmount, 0)

    -- Return the result of the function
    RETURN @TotalSalesAmount

我可以通过什么方式在我的存储过程选择中插入此 UDF 代码?或者我可以用什么方法使它成为 UDF_table 函数?

感谢您的帮助。

【问题讨论】:

  • 是的,用 UDF 的主体替换对标量 UDF 的调用,或者将 UDF 转换为内联表值 UDF 通常会大大提高性能。如果您能够将 UDF 的整个逻辑转换为单个 select 语句,您可以试一试。将其转换为多步表值 UDF 是没有意义的。
  • 谢谢。但我不知道如何将此逻辑与所有案例和计算放入单个选择语句中,该语句是用于填充#temp 表的子选择。你能指出我正确的方向吗?
  • 有很多方法,它们都取决于你在做什么。我将首先以with base_data as (SELECT p.Unit, case when p.SalesUnit = 0 then 1 else p.SalesUnit end as SalesUnit, dd.SalesUnitOfAccount FROM dbo.T_table p WHERE p.ColumnA = @A AND p.ColumnB = @B AND p.ColumnC = @C) select * from base_data 的形式定义一个 CTE,使其根据需要返回其他计算列(例如 case when dd.SalesUnitOfAccount = 4 then round((CurrPrice * (TotalSalesUnits / SalesComputQty)) - DiscAmount, 2) end,依赖于先前添加的计算列),然后从那里返回 select
  • 谢谢,我会试一试。对 SQL 非常陌生,所以很挣扎。

标签: sql sql-server stored-procedures user-defined-functions


【解决方案1】:

您所要求的正是 SQL Server 2019(CTP2.1 及更高版本)中最近宣布的一项名为“Scalar UDF Inlining”的功能的重点。此功能通过自动将 UDF 的逻辑嵌入(或内联)到调用查询中来工作。你可以免费下载试试看。

如果你想知道它是如何在幕后工作的,可以在最近的一篇研究论文“Froid: Optimization of Imperative programs in a Relational Database”中找到详细信息。该论文描述了一种将整个 UDF 表示为 SQL 的系统方法,您可以使用该方法。 Scalar UDF 内联功能基于 Froid,在许多情况下可以带来巨大的性能提升。

[披露:我是 Froid 论文的合著者]

【讨论】:

  • 确实是一个不错的功能。但是 SQL2019 不会在这里实现很久。坚持使用 2012 版本一段时间。
猜你喜欢
  • 2011-12-12
  • 1970-01-01
  • 2022-11-16
  • 1970-01-01
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多