Sql server 如何防止函数在Select语句中被调用两次

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

假设我有一个返回数字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)),但这意味着第一个函数被调用两次,导致开销增加。

经常,如果在处理多行的查询中使用用户定义的函数,则查询的性能会显著下降,因为服务器必须为每一行调用该函数。从这个意义上讲,优化查询和每行调用两个函数(而不是三个)不会对总体性能产生太大的影响,在任何情况下都会很差

如果您有一个非常复杂的函数,每次调用该函数都需要很长的时间,并且您希望调用有限的行数,那么优化调用的总体数量是有意义的

无论如何,我发现这个问题很有趣,足以检查我的猜测并写出我的发现


如果您使用的是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,我的答案或其他答案对您有用吗?请投票选出有用的答案,并接受最有用的答案。