• 优化案例--多语句表值函数的影响


    在SQL SERVER中,自定义函数可以划分成:

    1.内联表值函数

    2.多语句表值函数

    3.标量值函数

    上述三类自定义函数如果使用不当,就会造成性能问题,本片重点关注“多语句表值函数”。

    在多语句表值函数在每次调用时都需要使用到一个临时表来存放返回值,因此如果频繁调用该函数,会影响tempdb的性能。

    测试代码:

    --=========================================================================
    --创建测试表
    GO
    SELECT * INTO TB001 FROM sys.all_objects
    GO
    SELECT * INTO TB002 FROM sys.all_columns
    GO
    --=========================================================================
    --创建内联表值函数
    CREATE FUNCTION [dbo].[ufn_GetTop2Columns2]
    (    
        @object_ID BIGINT
    )
    RETURNS TABLE 
    AS
    RETURN 
    (
        SELECT TOP(2) name AS ColumnName 
        FROM TB002
        WHERE OBJECT_ID=@object_ID
    )
    GO
    --=========================================================================
    --多语句表值函数
    CREATE FUNCTION [dbo].[ufn_GetTop2Columns]
    (
        @object_ID BIGINT
    )
    RETURNS @result TABLE 
    (
        ColumnName NVARCHAR(200)
    )
    AS
    BEGIN
        INSERT INTO @result
        SELECT TOP(2) name AS ColumnName 
        FROM TB002
        WHERE OBJECT_ID=@object_ID
        RETURN 
    END
    GO
    SET STATISTICS IO ON
    SET STATISTICS TIME ON
    GO
    --=========================================================================
    --不使用表值函数
    SELECT *
    FROM TB001 AS T1
    CROSS APPLY (SELECT TOP(2) * 
    FROM TB002 AS T2 
    WHERE T1.Object_id=T2.Object_id ) AS T3
    --运行结果
    --表 'Worktable'。扫描计数 1989,逻辑读取 15095 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
    --表 'TB002'。扫描计数 1,逻辑读取 54 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
    --表 'TB001'。扫描计数 1,逻辑读取 34 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
    
    --SQL Server 执行时间:
    --CPU 时间 = 94 毫秒,占用时间 = 543 毫秒。
    
    
    --=========================================================================
    --使用多语句表值函数
    SELECT *
    FROM TB001 AS T1
    CROSS APPLY dbo.ufn_GetTop2Columns(T1.Object_id) AS T3
    --运行结果
    --表 '#756D6ECB'。扫描计数 1989,逻辑读取 1989 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
    --表 'TB001'。扫描计数 1,逻辑读取 34 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
    --SQL Server 执行时间:
    --CPU 时间 = 7129 毫秒,占用时间 = 7262 毫秒。
    --=========================================================================
    --内联表值函数
    SELECT *
    FROM TB001 AS T1
    CROSS APPLY dbo.ufn_GetTop2Columns2(T1.Object_id) AS T3
    --运行结果
    --表 'Worktable'。扫描计数 1989,逻辑读取 14736 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
    --表 'TB002'。扫描计数 1,逻辑读取 54 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
    --表 'TB001'。扫描计数 1,逻辑读取 34 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
    --SQL Server 执行时间:
    --CPU 时间 = 62 毫秒,占用时间 = 186 毫秒。

    多次运行发现,使用内联表值函数执行速度(186ms)快于未使用自定义函数的语句(543ms),而多语句表值函数的执行速度最慢(7262ms).

    优化建议:

    1. 将多语句表值函数改写成内联表值函数或不使用自定义函数的语句。

    2. 将CROSS APPLY改写成INNER JOIN ,以减少多语句表值函数的调用次数

    如将上面的语句改成:

    CREATE FUNCTION [dbo].[ufn_GetTop2Columns3]
    (
    )
    RETURNS @result TABLE 
    (
        ColumnName NVARCHAR(200),
        Object_id BIGINT
    )
    AS
    BEGIN
        INSERT INTO @result(ColumnName,Object_id)
        SELECT ColumnName,Object_id
        FROM
        (SELECT ROW_NUMBER()OVER(PARTITION BY T2.Object_id ORDER BY T2.Object_id) AS RID,
        T2.name AS ColumnName,
        T2.Object_id 
        FROM TB002 AS T2 
        ) AS T3
        WHERE RID<3
    
        RETURN 
    END
    GO
    --===================================================================
    --将CROSS APPLY改写成INNER JOIN,
    SELECT *
    FROM TB001 AS T1
    INNER JOIN [dbo].[ufn_GetTop2Columns3]() AS T3
    ON T1.Object_id=T3.Object_id
    --表 'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
    --表 'TB001'。扫描计数 1,逻辑读取 34 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
    --表 '#28ED12D1'。扫描计数 1,逻辑读取 5 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
    
    --SQL Server 执行时间:
    --CPU 时间 = 47 毫秒,占用时间 = 383 毫秒。

    当然,不是所有的多语句表值函数都可以被改写,在优化时测试各种优化方案,寻找到一种最适合业务场景的方法。

  • 相关阅读:
    BZOJ3813 奇数国
    BZOJ2735 世博会
    BZOJ2081 [Poi2010]Beads
    BZOJ3276 磁力
    BZOJ2054 疯狂的馒头
    BZOJ2610 [Poi2003]Monkeys
    BZOJ2428 [HAOI2006]均分数据
    BZOJ2120 数颜色
    BZOJ2527 [Poi2011]Meteors
    补比赛——牛客OI周赛9-普及组
  • 原文地址:https://www.cnblogs.com/TeyGao/p/3539629.html
Copyright © 2020-2023  润新知