You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复被百万次调用且重复生成执行计划的SQL Server函数

针对SQL Server函数重复编译问题的解决方案

一、能否将fn_TestBinaryBitwiseOr改为参数化存储过程?

不能。因为你的场景是在表值函数的SELECT语句中直接调用该函数获取列值,而存储过程无法嵌入到SELECT的列表达式中返回单个值——存储过程只能通过EXEC命令执行,无法作为列的计算逻辑嵌入查询,所以这个方案不适用你的场景。

二、可测试的替代解决方案

1. 将标量函数改为内联表值函数(ITVF)

标量值函数(Scalar UDF)在SQL Server中通常逐行执行,且易因参数值差异导致重复编译;而内联表值函数会被查询优化器直接展开到调用它的查询中,相当于将函数逻辑合并到主查询,既能减少编译次数,又能提升执行效率。

修改后的函数代码:

CREATE OR ALTER FUNCTION[dbo].[fn_TestBinaryBitwiseOr_ITVF]
    (@TestFlags1 binary(16),
     @TestFlags2 binary(16))
RETURNS TABLE
AS
RETURN
SELECT 
    CASE
        WHEN @TestFlags1 IS NULL AND @TestFlags2 IS NULL 
            THEN 0x0
        WHEN @TestFlags1 IS NULL 
            THEN @TestFlags2
        WHEN @TestFlags2 IS NULL 
            THEN @TestFlags1
        ELSE
            CONVERT(binary(16), 
                CONVERT(binary(4), (SUBSTRING(@TestFlags2, 1, 4) | CONVERT(bigint, SUBSTRING(@TestFlags1, 1, 4)))) +  
                CONVERT(binary(4), (SUBSTRING(@TestFlags2, 5, 4) | CONVERT(bigint, SUBSTRING(@TestFlags1, 5, 4)))) +  
                CONVERT(binary(4), (SUBSTRING(@TestFlags2, 9, 4) | CONVERT(bigint, SUBSTRING(@TestFlags1,9,4)))) +
                CONVERT(binary(4), (SUBSTRING(@TestFlags2, 13, 4) | CONVERT(bigint, SUBSTRING(@TestFlags1, 13, 4)))) 
            )
    END AS Result
GO

调整DoAThing函数的调用方式(使用CROSS APPLY):

INSERT INTO @T(ColName) 
SELECT itvf.Result
FROM @Table1 t1 
JOIN @Table2 t2 ON t1.id = t2.table1Id
CROSS APPLY dbo.fn_TestBinaryBitwiseOr_ITVF(t1.binary1, t2.binary2) itvf

2. 创建模板计划指南强制参数化

针对该函数的调用模式创建模板计划指南,无需开启全局强制参数化,仅对特定查询模式生效,避免全局设置的副作用。

示例代码(需替换为真实查询文本和参数定义):

EXEC sp_create_plan_guide 
    @name = N'Guide_fn_TestBinaryBitwiseOr',
    @stmt = N'SELECT [dbo].[fn_TestBinaryBitwiseOr](t1.binary1, t2.binary2) AS result FROM @Table1 t1 JOIN @Table2 t2 ON t1.id = t2.table1Id',
    @type = N'TEMPLATE',
    @module_or_batch = NULL,
    @params = N'@Table1 table (id int, binary1 binary(16)), @Table2 table (table1Id int, binary2 binary(16))',
    @hints = N'OPTION(PARAMETERIZATION FORCED)'

3. 优化原标量函数的内部逻辑

原函数中的SELECT TOP(1)属于冗余代码(CASE语句仅返回单个值),去掉后可简化执行逻辑,帮助优化器更稳定地复用执行计划:

优化后的标量函数:

CREATE OR ALTER FUNCTION[dbo].[fn_TestBinaryBitwiseOr]
    (@TestFlags1 binary(16),
     @TestFlags2 binary(16))
RETURNS binary(16)
AS
BEGIN
    RETURN 
        CASE
            WHEN @TestFlags1 IS NULL AND @TestFlags2 IS NULL 
                THEN 0x0
            WHEN @TestFlags1 IS NULL 
                THEN @TestFlags2
            WHEN @TestFlags2 IS NULL 
                THEN @TestFlags1
            ELSE
                CONVERT(binary(16), 
                    CONVERT(binary(4), (SUBSTRING(@TestFlags2, 1, 4) | CONVERT(bigint, SUBSTRING(@TestFlags1, 1, 4)))) +  
                    CONVERT(binary(4), (SUBSTRING(@TestFlags2, 5, 4) | CONVERT(bigint, SUBSTRING(@TestFlags1, 5, 4)))) +  
                    CONVERT(binary(4), (SUBSTRING(@TestFlags2, 9, 4) | CONVERT(bigint, SUBSTRING(@TestFlags1,9,4)))) +
                    CONVERT(binary(4), (SUBSTRING(@TestFlags2, 13, 4) | CONVERT(bigint, SUBSTRING(@TestFlags1, 13, 4)))) 
                )
        END
END 
GO

4. 持久化计算列(如果适用)

若binary1和binary2来自物理表而非临时表,可将函数计算逻辑定义为持久化计算列,计算结果会直接存储在表中,无需每次调用函数计算:

示例(基于物理表创建绑定视图并持久化):

-- 创建绑定视图
CREATE VIEW vw_BinaryOrResult WITH SCHEMABINDING
AS
SELECT 
    t1.id,
    CONVERT(binary(16), 
        CONVERT(binary(4), (SUBSTRING(t2.binary2, 1, 4) | CONVERT(bigint, SUBSTRING(t1.binary1, 1, 4)))) +  
        CONVERT(binary(4), (SUBSTRING(t2.binary2, 5, 4) | CONVERT(bigint, SUBSTRING(t1.binary1, 5, 4)))) +  
        CONVERT(binary(4), (SUBSTRING(t2.binary2, 9, 4) | CONVERT(bigint, SUBSTRING(t1.binary1,9,4)))) +
        CONVERT(binary(4), (SUBSTRING(t2.binary2, 13, 4) | CONVERT(bigint, SUBSTRING(t1.binary1, 13, 4)))) 
    ) AS BinaryOrResult
FROM dbo.Table1 t1
JOIN dbo.Table2 t2 ON t1.id = t2.table1Id

-- 创建持久化索引(需视图包含唯一键)
CREATE UNIQUE CLUSTERED INDEX IX_vw_BinaryOrResult ON vw_BinaryOrResult(id)

后续查询直接从视图获取结果即可,无需调用函数。


内容的提问来源于stack exchange,提问作者kosmos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 02:40:31