SQL Server自定义函数怎么写?附实用实例详解

来源:Docker教程作者:苏沐橙头衔:网络博主
导读:本期聚焦于小伙伴创作的《SQL Server自定义函数怎么写?附实用实例详解》,敬请观看详情。在写报表查询时,常常要把一串字符按分隔符拆成多行,或者反复计算某个业务指标。如果每次都在存储过程里拼逻辑,不仅啰嗦还难维护。SQL Server提供的自定义函数(UDF)能把这类复用逻辑封装起来,分为标量函数和内联表值函数两类。标量函数接收参数后返回单个值,适合做格式化或计算;表值函数返回结果集,可直接用在FROM子句里。下面通过拆分字符串和统计订单金额两个例子,说明创建语法、调用方式以及使用时的性能注意点,帮助你把重复SQL收敛成简洁调用。

SQL Server自定义函数(User Defined Function,简称UDF)是数据库中用于封装可复用逻辑的对象。它允许开发者将复杂的计算、字符串处理或数据转换逻辑写成独立模块,在查询、视图或存储过程中像系统函数一样调用。按照返回结果形态,主要分为标量值函数(返回单个值)和表值函数(返回行集),后者又包括内联表值函数和多语句表值函数。理解它们的差异并掌握书写规范,能显著提升SQL代码的可维护性。

SQL Server自定义函数怎么写?附实用实例详解

一、标量值函数实例:计算带税金额

标量函数最常见的用途是封装固定计算公式。例如电商系统中,订单金额需要根据不同税率计算含税价。如果每次查询都写一遍乘法和四舍五入,既容易出错也不利于税率调整。我们可以创建一个接收金额和税率、返回小数的函数。

下面的示例创建了一个名为dbo.fn_CalcTax的标量函数,它使用RETURNS指定返回类型,在函数体内通过RETURN返回计算结果。注意在函数中不能使用会产生副作用的语句,比如INSERT或UPDATE,只能做纯计算。

CREATE FUNCTION dbo.fn_CalcTax
(
    @Amount DECIMAL(10,2),
    @Rate DECIMAL(4,2)
)
RETURNS DECIMAL(10,2)
AS
BEGIN
    DECLARE @Result DECIMAL(10,2);
    -- 计算含税金额并保留两位小数
    SET @Result = ROUND(@Amount * (1 + @Rate), 2);
    RETURN @Result;
END;

调用方式非常直观,可以放在SELECT列表里直接使用:

SELECT 
    OrderID,
    Amount,
    dbo.fn_CalcTax(Amount, 0.13) AS TaxAmount
FROM dbo.Orders;

这种写法的好处是业务逻辑集中管理,当税率规则变化(如增加减免逻辑)时,只需修改函数本身,所有调用处自动生效。不过需要注意,在大数据量查询中,逐行调用标量函数可能带来一定的CPU开销,后续会提到优化思路。

二、内联表值函数实例:拆分逗号分隔字符串

很多遗留系统把多个ID存进一个带逗号的字段,查询时又需要按行展开。内联表值函数(Inline TVF)非常适合这种场景,它在结构上类似带参数的视图,SQL Server能将其逻辑合并到主查询执行计划中,性能优于多语句表值函数。

以下函数接收字符串和分隔符,利用STRING_SPLIT(SQL Server 2016及以上支持)返回拆分后的行集。由于是内联形式,没有BEGIN/END包裹的函数体,直接RETURN一个SELECT即可。

CREATE FUNCTION dbo.fn_SplitString
(
    @Str NVARCHAR(MAX),
    @Delimiter NCHAR(1)
)
RETURNS TABLE
AS
RETURN
(
    SELECT 
        value AS Item,
        ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS Seq
    FROM STRING_SPLIT(@Str, @Delimiter)
);

使用时把它当作一张表,通过CROSS APPLY关联主表:

SELECT 
    u.UserID,
    s.Item AS RoleID
FROM dbo.Users u
CROSS APPLY dbo.fn_SplitString(u.RoleList, ',') s;

内联表值函数不会生成独立的临时结果集,优化器能把STRING_SPLIT的扫描直接推入主查询,因此面对百万级数据也比自定义循环拆分快很多。如果要在更早版本实现,可以用递归CTE写在函数里,但同样建议保持内联结构。

三、多语句表值函数与适用边界

当返回结果需要复杂加工(比如先过滤再聚合再关联)时,可以使用多语句表值函数。它内部有BEGIN/END块,先声明表变量,再插入数据,最后RETURN该变量。虽然灵活,但优化器难以预估行数,容易生成次优计划。

示例:返回一个部门下所有员工的汇总信息。

CREATE FUNCTION dbo.fn_DeptSummary
(
    @DeptID INT
)
RETURNS @Result TABLE
(
    EmpID INT,
    EmpName NVARCHAR(50),
    TotalSales DECIMAL(12,2)
)
AS
BEGIN
    INSERT INTO @Result
    SELECT 
        e.EmpID,
        e.EmpName,
        ISNULL(SUM(s.Amount), 0)
    FROM dbo.Employee e
    LEFT JOIN dbo.Sales s ON e.EmpID = s.EmpID
    WHERE e.DeptID = @DeptID
    GROUP BY e.EmpID, e.EmpName;

    RETURN;
END;

调用时同样放在FROM中:

SELECT * FROM dbo.fn_DeptSummary(3);

要注意,多语句表值函数因表变量缺乏统计信息,在传入参数变化大时可能走错误的执行计划。若性能敏感,建议改写为内联表值函数或直接使用带参数的视图、存储过程输出结果集。

四、自定义函数的性能与维护建议

使用UDF时,有几个实践原则能减少后期麻烦。第一,标量函数尽量避免在WHERE条件里包裹列,例如WHERE dbo.fn_GetYear(OrderDate)=2023会阻止索引查找,应改成范围比较。第二,优先选择内联表值函数替代游标或循环拆分逻辑。第三,函数内不要访问其他表以外的外部资源,保持纯计算或数据集操作。

另外,函数一旦被视图或计算列引用,修改定义前需先删除依赖对象。可以通过sys.sql_expression_dependencies查看引用链。合理的函数粒度应该是单一职责:一个函数只解决一类转换,不要写成几十行的大杂烩,否则测试和排错成本会急剧上升。

通过上面三个实例可以看出,SQL Server自定义函数并不是万能胶,而是把确定性逻辑从重复SQL中抽离出来的工具。在报表、接口层和数据清洗环节用对地方,代码量和运维负担都能明显下降。

SQL_Server自定义函数UDF修改时间:2026-08-10 10:03:36

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。