Sql server 如何防止函数在Select语句中被调用两次
假设我有一个返回数字dbo.somefunction(@id INT)的函数。然后我有另一个函数,返回一点。dbo.returnsbit(@id INT) 这就是我的观点Sql server 如何防止函数在Select语句中被调用两次,sql-server,tsql,Sql Server,Tsql,假设我有一个返回数字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
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)),但这意味着第一个函数被调用两次,导致开销增加。经常,如果在处理多行的查询中使用用户定义的函数,则查询的性能会显著下降,因为服务器必须为每一行调用该函数。从这个意义上讲,优化查询和每行调用两个函数(而不是三个)不会对总体性能产生太大的影响,在任何情况下都会很差 如果您有一个非常复杂的函数,每次调用该函数都需要很长的时间,并且您希望调用有限的行数,那么优化调用的总体数量是有意义的 无论如何,我发现这个问题很有趣,足以检查我的猜测并写出我的发现
如果您使用的是SQL Server 2005或更高版本,则可以使用
交叉应用
SELECT
CA.returnIntValue
,dbo.returnsbit(CA.returnIntValue) As BitValue
FROM
sometable
CROSS APPLY
(
SELECT dbo.somefunction(id) AS returnIntValue
) AS CA
我在SQLServer2008上检查过,该变体确实每行只调用一次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
-963307489 -963307489
450369380 450369380
1193334688 1193334688
1480723291 1480723291
-1666937401 -1666937401
1001969991 1001969991
-1142557574 -1142557574
-1891218324 -1891218324
-102288163 -102288163
1575326336 1575326336
您可以看到每行中的值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
-1111498263 -1481060640
1678801669 230929974
1377897182 -1527788053
1786076194 -301754441
734901522 1385475384
-636644847 -1076939672
1551114591 -385251162
32984627 -1214863465
2075259001 -1450159610
-2063202107 -1023434184
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
。您可以执行以下操作:
SELECT returnIntValue, dbo.returnsbit(returnIntValue) As BitValue
FROM
(SELECT dbo.somefunction(id) AS returnIntValue
FROM sometable) t
我猜dbo.somefunction可能仍然会在每一行上被调用两次,这取决于优化器选择的查询计划,正如不信者Damien所指出的那样。您是否存在实际的性能问题?过早优化是新手犯下的最大错误。除了布拉姆的评论之外,你还需要了解,通常在SQL中,你告诉系统你想要什么,而不是怎么做。您应该尝试编写尽可能清晰地表达您的目标的代码,并且只有当它没有达到您的性能目标时,您才应该开始研究性能最差的部分(这部分实际上适用于每种语言)@user79284,我的答案或其他答案对您有用吗?请投票选出有用的答案,并接受最有用的答案。