【问题标题】:Is a scalar database function used in a join called once per distinct set of inputs or once per row?连接中使用的标量数据库函数是每个不同的输入集调用一次还是每行调用一次?
【发布时间】:2015-05-26 19:47:39
【问题描述】:

如果我有这样的 sql 语句:

select * 
from tableA a
inner join tableB b on dbo.fn_something(a.ColX) = b.ColY

如果您假设 tableA 中有 5 行与 ColX 的值相同,那么 dbo.fn_something() 会以该值调用 5 次还是仅调用一次?

显然,这是一个微不足道的例子,但我感兴趣的是考虑在更复杂的场景中的性能。

更新 感谢@DStanley,根据您的回答,我进一步调查了。在下面的 SQL 上使用带有 SP:StmtStarting 事件的 SQL Profiler 说明了会发生什么。即如您所说:该函数将为联接中的每一行调用一次。

这与原始问题有一个额外的连接。

create table tableA 
( id int ) 

create table tableB
( id_a int not null 
, id_c int not null 
) 

create table tableC
( id int ) 

go 
create function dbo.fn_something( @id int ) 
returns int 
as 
begin 
    return @id
end 
go 

-- add test data

-- 5 rows:
insert into tableA (id) values (1), (2), (3), (4), (5) 
-- 5 rows:
insert into tableC (id) values (101), (102), (103), (104), (105) 
-- 25 rows:
insert into tableB (id_a, id_c) select a.id, c.id from tableA a, tableC c

go 

-- here dbo.fn_something() is called 25 times: 
select * 
from tableA a
inner join tableB b on a.id = b.id_a
inner join tableC c on c.id = dbo.fn_something(b.id_c)

-- here dbo.fn_something() is called just 5 times, 
-- as the 'b.id_c < 102' happens to be applied first.
-- That's likely to depend on whether SQL thinks it's 
-- faster to evaluate the '<' or the function. 
select * 
from tableA a
inner join tableB b on a.id = b.id_a
inner join tableC c on c.id = dbo.fn_something(b.id_c) and b.id_c < 102

go

drop table tableA ; 
drop table tableB; 
drop table tableC; 
drop function dbo.fn_something;
go 

【问题讨论】:

    标签: sql sql-server tsql


    【解决方案1】:

    它将为a 中的每一行调用。我不知道任何优化只会为唯一输入调用该函数。 如果性能是一个问题,您可以创建一个具有不同输入值的临时表并在您的联接中使用这些结果,但我只会这样做,这是一个问题 - 不要 假设 这是一个问题,并且不必要地使您的查询混乱。

    【讨论】:

    • 答案不取决于比这更多的细节吗? IE,内联 TVF 与多语句 TVF?
    • 在@sqlacid here stackoverflow.com/a/352615/8479 的帮助下,我使用 SQL Profiler 的 SP:StmtStarting 来验证连接中的每一行都调用了 dbo.fn_something
    • @BradD,我只对标量函数感兴趣,所以相应地更新了标题。
    • @BradD 根据用法,我假设一个标量函数。
    • 另外,如果您正在编写标量函数作为连接的一部分,我会认真考虑修复数据库设计。你永远不应该有一个不能直接加入你打算加入的字段的数据库(它们通常都应该被索引)。如果必须的话,最好有一个持久的计算字段,所以计算只在输入或更改数据时进行,而不是每次在连接中调用它。
    【解决方案2】:

    如果您将函数声明为模式绑定,则可以针对每个唯一情况运行一个。这要求函数是确定性的,并且对于给定的输入始终具有相同的输出。

    CREATE FUNCTION dbo.fn_something (@id INT)
    RETURNS INT
    WITH SCHEMABINDING
    AS
    BEGIN
        RETURN @id
    END
    GO
    

    【讨论】:

    • 虽然确实需要 SCHEMABINDING,但您确定效果是每个唯一输入值只运行一次吗?你测试过吗?我的测试表明 T-SQL UDF(您在此处显示的那个)每行运行一次,无论值如何,也无论是否使用 WITH SCHEMABINDING
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-07-21
    • 1970-01-01
    • 2023-03-26
    • 2019-10-12
    • 2023-03-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多