假设我有一个函数返回一个数字dbo.somefunction(@id INT)。然后我有另一个函数返回一点。 dbo.returnsbit(@id INT)
这就是我的观点
SELECT dbo.somefunction(id) AS returnIntValue
FROM sometable
最终这就是我想做的事情
SELECT dbo.somefunction(id) AS returnIntValue,, dbo.returnsbit(returnIntValue) As BitValue
FROM sometable
问题是,第二个函数(dbo.returnsbit)使用第一个函数(dbo.somefunction)返回的值。当然我可以做dbo.returnsbit(dbo.somefunction(id)),但这意味着第一个函数被调用两次导致开销增加。
答案 0 :(得分:3)
通常,如果在查询中使用用户定义的函数来处理许多行,则查询的性能会显着下降,因为服务器必须为每一行调用函数。从这个意义上说,优化查询并且每行调用两个函数而不是三个函数不会改变整体性能,在任何情况下它都会很差。
如果你有一个非常复杂的函数,函数的每次调用需要很长时间,并且你希望将它调用为有限数量的行,那么优化调用的总数是有意义的。
无论如何,我发现这个问题很有趣,可以检查我的猜测并写下我的发现。
如果您使用的是SQL Server 2005或更高版本,则可以使用CROSS APPLY
。
SELECT
CA.returnIntValue
,dbo.returnsbit(CA.returnIntValue) As BitValue
FROM
sometable
CROSS APPLY
(
SELECT dbo.somefunction(id) AS returnIntValue
) AS CA
我已经检查过SQL Server 2008,这个变体确实每行只调用somefunction
一次。
有时候,从性能的角度来看,这一点很重要。如果带有副作用的函数被调用的次数多于所需的次数,则可能会得到不同的结果。见下面的例子。
以下是确认CROSS APPLY
每行只调用一次somefunction
的方法。
创建一个包含少量行的表
CREATE TABLE [dbo].[Numbers]([Number] [int] NOT NULL);
INSERT INTO [dbo].[Numbers] ([Number])
VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
创建第一个功能。它将返回一个随机数。每次调用时都会有不同的数字。它不会使用参数,但对于此示例无关紧要。
CREATE VIEW [dbo].[ViewRnd]
AS
SELECT CAST(CRYPT_GEN_RANDOM(4) AS int) AS rnd
CREATE FUNCTION [dbo].[somefunction]
(
@id int
)
RETURNS int
AS
BEGIN
DECLARE @Result int;
SELECT @Result = rnd FROM dbo.ViewRnd;
RETURN @Result;
END
创建第二个功能。对于此示例,它将只返回给定的参数。
CREATE FUNCTION [dbo].[somefunction2]
(
@id int
)
RETURNS int
AS
BEGIN
DECLARE @Result int;
SELECT @Result = @id;
RETURN @Result;
END
每行两次调用的第一个查询:
SELECT
dbo.somefunction(dbo.Numbers.Number) AS f1
, dbo.somefunction2(dbo.somefunction(dbo.Numbers.Number)) AS f2
FROM dbo.Numbers
;
结果集:
f1 f2
-1111498263 -1481060640
1678801669 230929974
1377897182 -1527788053
1786076194 -301754441
734901522 1385475384
-636644847 -1076939672
1551114591 -385251162
32984627 -1214863465
2075259001 -1450159610
-2063202107 -1023434184
您可以看到每行中的值f1
和f2
不同,这意味着每行生成两次随机数,即函数somefunction
每次调用两次行。
每行一次调用的第二个查询:
SELECT
CA.f1
, dbo.somefunction2(CA.f1) AS f2
FROM
dbo.Numbers
CROSS APPLY
(
SELECT dbo.somefunction(dbo.Numbers.Number) AS f1
) CA
;
结果集:
f1 f2
-963307489 -963307489
450369380 450369380
1193334688 1193334688
1480723291 1480723291
-1666937401 -1666937401
1001969991 1001969991
-1142557574 -1142557574
-1891218324 -1891218324
-102288163 -102288163
1575326336 1575326336
您可以看到每行中的值f1
和f2
相同,这意味着每行只调用一次函数somefunction
。
答案 1 :(得分:0)
你可以这样做:
SELECT returnIntValue, dbo.returnsbit(returnIntValue) As BitValue
FROM
(SELECT dbo.somefunction(id) AS returnIntValue
FROM sometable) t
我猜dbo.somefunction可能仍然会在每行调用两次,具体取决于优化程序选择的查询计划,正如Damien_The_Unbeliever指出的那样。