In the following t-sql statement, how many times will the dbo.FUNC function get called?
在下面的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
Will it called multiple separate times per row, or does the optimizer recognize that it is being used multiple times in a single statement, and only call it once?
它会在每行调用多个单独的时间,还是优化器会识别出它在单个语句中被多次使用,并且只调用一次?
How can I test this? I can't insert into a table inside of a function, so incrementing a counter wont work...
我该怎么测试呢?我无法插入函数内的表中,因此递增计数器不会工作...
3 个解决方案
#1
11
This isn't guaranteed.
这不保证。
You would need to check the execution plan to find out. Some examples.
您需要检查执行计划以找出答案。一些例子。
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
FUNC2
is created WITH SCHEMABINDING
and is treated as deterministic. FUNC1
isn't.
FUNC2是使用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)
Gives Plan
|--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]))
FUNC1
is evaluated twice (once in the filter and once in a compute scalar outputting a calculated column used for both the projection and the ordering), FUNC2
is only evaluated once.
FUNC1被评估两次(一次在滤波器中,一次在计算标量输出用于投影和排序的计算列),FUNC2仅被评估一次。
Rewriting as
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
Changes the plan slightly and both are only evaluated once
略微更改计划,两者仅评估一次
|--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]))
Now making a slight alteration to the query
现在对查询稍作修改
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
Gives the opposite of the original result in that FUNC2
is evaluated twice but FUNC1
only once.
与原始结果相反,FUNC2被评估两次但FUNC1仅被评估一次。
|--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]))
#2
1
Firstly, it depends on if the function is deterministic.
首先,它取决于函数是否是确定性的。
Even then, that will only be used for multiple calls on a single row.
即便如此,这只会用于单行上的多个调用。
I believe your case would be optimized if the function is deterministic.
如果函数是确定性的,我相信你的情况会被优化。
#3
-2
yes.
the optimizer has the sufficient knowledge to optimize this into a same calc while running.
优化器具有足够的知识,可以在运行时将其优化为相同的calc。
you can look at the execution plan to see it.
您可以查看执行计划以查看它。
#1
11
This isn't guaranteed.
这不保证。
You would need to check the execution plan to find out. Some examples.
您需要检查执行计划以找出答案。一些例子。
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
FUNC2
is created WITH SCHEMABINDING
and is treated as deterministic. FUNC1
isn't.
FUNC2是使用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)
Gives Plan
|--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]))
FUNC1
is evaluated twice (once in the filter and once in a compute scalar outputting a calculated column used for both the projection and the ordering), FUNC2
is only evaluated once.
FUNC1被评估两次(一次在滤波器中,一次在计算标量输出用于投影和排序的计算列),FUNC2仅被评估一次。
Rewriting as
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
Changes the plan slightly and both are only evaluated once
略微更改计划,两者仅评估一次
|--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]))
Now making a slight alteration to the query
现在对查询稍作修改
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
Gives the opposite of the original result in that FUNC2
is evaluated twice but FUNC1
only once.
与原始结果相反,FUNC2被评估两次但FUNC1仅被评估一次。
|--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]))
#2
1
Firstly, it depends on if the function is deterministic.
首先,它取决于函数是否是确定性的。
Even then, that will only be used for multiple calls on a single row.
即便如此,这只会用于单行上的多个调用。
I believe your case would be optimized if the function is deterministic.
如果函数是确定性的,我相信你的情况会被优化。
#3
-2
yes.
the optimizer has the sufficient knowledge to optimize this into a same calc while running.
优化器具有足够的知识,可以在运行时将其优化为相同的calc。
you can look at the execution plan to see it.
您可以查看执行计划以查看它。