当对同一个UDF的多次调用都在一个语句中时,它会被调用多少次?

Gab*_*ams 5 t-sql sql-server

在下面的t-sql语句中,dbo.FUNC函数被调用多少次?

SELECT
    column1,
    column2,
    dbo.FUNC(column3) AS column3
FROM table1
WHERE dbo.FUNC(column3) >= 5
ORDER BY dbo.FUNC(column3) DESC
Run Code Online (Sandbox Code Playgroud)

它会在每行调用多个单独的时间,还是优化器会识别出它在单个语句中被多次使用,并且只调用一次?

我该怎么测试呢?我无法插入函数内的表格,因此递增计数器不会工作...

Mar*_*ith 12

这不保证.

您需要检查执行计划以找出答案.一些例子.

CREATE FUNCTION dbo.FUNC1(@p1 int)
RETURNS int
AS
BEGIN
    RETURN @p1 + 1
END

GO

CREATE FUNCTION dbo.FUNC2(@p1 int)
RETURNS int
WITH SCHEMABINDING
AS
BEGIN
    RETURN @p1 + 1
END

GO
SELECT 
       OBJECTPROPERTYEX(OBJECT_ID('dbo.FUNC1'), 'IsDeterministic'),
       OBJECTPROPERTYEX(OBJECT_ID('dbo.FUNC2'), 'IsDeterministic') 
GO
Run Code Online (Sandbox Code Playgroud)

FUNC2已创建WITH SCHEMABINDING并被视为确定性.FUNC1不是.

SELECT
    dbo.FUNC1(number) AS FUNC1,
    dbo.FUNC2(number) AS FUNC2
FROM master..spt_values
WHERE dbo.FUNC1(number) >= 5 AND dbo.FUNC2(number) >= 5
ORDER BY dbo.FUNC1(number), dbo.FUNC2(number)
Run Code Online (Sandbox Code Playgroud)

给予计划

PLAN1

  |--Sort(ORDER BY:([Expr1003] ASC, [Expr1004] ASC))
       |--Compute Scalar(DEFINE:([Expr1003]=[test].[dbo].[FUNC1]([master].[dbo].[spt_values].[number])))
            |--Filter(WHERE:([test].[dbo].[FUNC1]([master].[dbo].[spt_values].[number])>=(5) AND [Expr1004]>=(5)))
                 |--Compute Scalar(DEFINE:([Expr1004]=[test].[dbo].[FUNC2]([master].[dbo].[spt_values].[number])))
                      |--Index Scan(OBJECT:([master].[dbo].[spt_values].[ix2_spt_values_nu_nc]))
Run Code Online (Sandbox Code Playgroud)

FUNC1被评估两次(一次在过滤器中,一次在计算标量输出用于投影和排序的计算列),FUNC2仅评估一次.

重写为

SELECT
    FUNC1,
    FUNC2
FROM master..spt_values
CROSS APPLY (SELECT dbo.FUNC1(number), dbo.FUNC2(number)) C(FUNC1, FUNC2)
WHERE FUNC1 >= 5 AND FUNC2 >= 5
ORDER BY FUNC1, FUNC2
Run Code Online (Sandbox Code Playgroud)

略微更改计划,两者仅评估一次

计划2

  |--Sort(ORDER BY:([Expr1003] ASC, [Expr1004] ASC))
       |--Filter(WHERE:([Expr1003]>=(5)))
            |--Compute Scalar(DEFINE:([Expr1003]=[test].[dbo].[FUNC1]([master].[dbo].[spt_values].[number])))
                 |--Filter(WHERE:([Expr1004]>=(5)))
                      |--Compute Scalar(DEFINE:([Expr1004]=[test].[dbo].[FUNC2]([master].[dbo].[spt_values].[number])))
                           |--Index Scan(OBJECT:([master].[dbo].[spt_values].[ix2_spt_values_nu_nc]))
Run Code Online (Sandbox Code Playgroud)

现在对查询稍作修改

SELECT
    FUNC1 + 10,
    FUNC2 + 10
FROM master..spt_values
CROSS APPLY (SELECT dbo.FUNC1(number), dbo.FUNC2(number)) C(FUNC1, FUNC2)
WHERE FUNC1 >= 5 AND FUNC2 >= 5
ORDER BY FUNC1, FUNC2
Run Code Online (Sandbox Code Playgroud)

给出与原始结果相反的结果,即FUNC2两次评估但FUNC1仅一次.

计划3

  |--Compute Scalar(DEFINE:([Expr1005]=[Expr1003]+(10)))
       |--Sort(ORDER BY:([Expr1003] ASC, [Expr1004] ASC))
            |--Filter(WHERE:([Expr1003]>=(5)))
                 |--Compute Scalar(DEFINE:([Expr1003]=[test].[dbo].[FUNC1]([master].[dbo].[spt_values].[number])))
                      |--Filter(WHERE:([Expr1004]>=(5)))
                           |--Compute Scalar(DEFINE:([Expr1004]=[test].[dbo].[FUNC2]([master].[dbo].[spt_values].[number]), [Expr1006]=[test].[dbo].[FUNC2]([master].[dbo].[spt_values].[number])+(10)))
                                |--Index Scan(OBJECT:([master].[dbo].[spt_values].[ix2_spt_values_nu_nc]))
Run Code Online (Sandbox Code Playgroud)