使用临时 table 优化递归 UDF
Optimizing a recursive UDF with a temp table
我有以下函数循环遍历树结构以查看是否有任何子注释包含 属性:
CREATE FUNCTION [dbo].[ufn_FamilyHasCar] ( @PersonId Integer ) RETURNS bit
as
BEGIN
DECLARE @Out bit = 0
Declare @Count int
Select @Count = count(*) from car WHERE owner_id = @PersonId and type = 2
if @Count = 0
begin
declare @tbl_temp table (personId int)
Declare @Id int
insert into @tbl_temp(personId) (select id from person where parent_id = @PersonId)
While (Select Count(*) From @tbl_temp) > 0
Begin
Select Top 1 @Id = personId From @tbl_temp
set @Out = dbo.ufn_FamilyHasCar(@Id)
if @Out = 1
break
Delete from @tbl_temp Where personId = @Id
End
end
else
set @Out = 1
RETURN @Out
END
GO
这似乎是我目前实现的瓶颈,所以我想问一下我是否以及如何提高 udf 的性能?
我会用递归 CTE 替换它:
with pp as (
select p.id
from person p
where p.id = @personid
union all
select p.id
from pp join
person p
on pp.parentid = p.id
)
select (case when count(*) > 0 then 1 else 0 end)
from pp join
car c
on pp.id = c.owner_id and type = 2;
COUNT(*)
是表达查询的最清晰的方式。但是,如果有很多匹配项(比如说超过几十个),那么聚合的额外开销就太过分了,您可以改用 exists:
with pp as ( . . .)
select (case when exists (select 1 from pp join car cc on pp.id = c.owner_id and type = 2)
then 1 else 0
end);
我有以下函数循环遍历树结构以查看是否有任何子注释包含 属性:
CREATE FUNCTION [dbo].[ufn_FamilyHasCar] ( @PersonId Integer ) RETURNS bit
as
BEGIN
DECLARE @Out bit = 0
Declare @Count int
Select @Count = count(*) from car WHERE owner_id = @PersonId and type = 2
if @Count = 0
begin
declare @tbl_temp table (personId int)
Declare @Id int
insert into @tbl_temp(personId) (select id from person where parent_id = @PersonId)
While (Select Count(*) From @tbl_temp) > 0
Begin
Select Top 1 @Id = personId From @tbl_temp
set @Out = dbo.ufn_FamilyHasCar(@Id)
if @Out = 1
break
Delete from @tbl_temp Where personId = @Id
End
end
else
set @Out = 1
RETURN @Out
END
GO
这似乎是我目前实现的瓶颈,所以我想问一下我是否以及如何提高 udf 的性能?
我会用递归 CTE 替换它:
with pp as (
select p.id
from person p
where p.id = @personid
union all
select p.id
from pp join
person p
on pp.parentid = p.id
)
select (case when count(*) > 0 then 1 else 0 end)
from pp join
car c
on pp.id = c.owner_id and type = 2;
COUNT(*)
是表达查询的最清晰的方式。但是,如果有很多匹配项(比如说超过几十个),那么聚合的额外开销就太过分了,您可以改用 exists:
with pp as ( . . .)
select (case when exists (select 1 from pp join car cc on pp.id = c.owner_id and type = 2)
then 1 else 0
end);