加入标量函数的结果导致的性能问题?

ber*_*d_k 1 performance sql-server-2008 temporary-tables

在以下示例中,仅使用一个 SQL 查询的第一个版本的性能非常差。问题是最后一次加入。

当我将左侧的结果存储到一个临时表中并加入它时,它的运行速度大约快了 50 倍。

它旨在将其转换为 Oracle,因此我尽量避免使用临时表。

任何解释,为什么第一个查询表现如此糟糕或暗示改进它?

Declare @HO int = 2866;
Declare @Dtz int = 35;

---- version 1 takes about 60 seconds
With ratings as
(
    select
        ROW_NUMBER() Over ( ORDER BY SEDate) lfd, 
        SEDate, 
        BBCode 
    from  PPV
        join BB on BBRefnr = SEBBRefnr
    where SEHORefnr = @HORefnr 
    and SEBBRefnr > 0
)
Select
    SEDATE,
    BBCODE,
    Code,
    DTRefnr
FROM (  
Select 
    l.SEDate,
    l.BBCode,
    dbo.GetCode(@Dtz, l.BBCode) Code
from ratings l left join ratings r on l.lfd = r.lfd + 1
where l.BBCode <> r.BBCode
or r.BBCode is null
) as t
    join DT d on code = d.DTCode and d.DTDTKRefnr = @Dtz 
order by 1;

------- version 2 using a temp table takes less than 1 second

With ratings as
(
    select
        ROW_NUMBER() Over ( ORDER BY SEDate) lfd, 
        SEDate, 
        BBCode 
    from  PPV
        join BB on BBRefnr = SEBBRefnr
    where SEHORefnr = @HORefnr 
    and SEBBRefnr > 0
)
Select 
    l.SEDate,
    l.BBCode,
    dbo.GetCode(@Dtz, l.BBCode) Code
into #tmp
from ratings l left join ratings r on l.lfd = r.lfd + 1
where l.BBCode <> r.BBCode
or r.BBCode is null


Select
    SEDATE,
    BBCODE,
    Code,
    DTRefnr
FROM #tmp
    join DT d on code = d.DTCode and d.DTDTKRefnr = @Dtz 
order by 1;
Run Code Online (Sandbox Code Playgroud)

编辑:

这里的功能:

Create function dbo.GetCode (
    @fDtz int, 
    @Value varchar(10)
) returns varchar(10)
as
begin
    declare @CODE varchar(10)
    SET @Code =
        (SELECT Code FROM Ableitungen 
        WHERE DTZ = @fDtz
        AND Value = @Value )

    return @CODE
end
Run Code Online (Sandbox Code Playgroud)

gbn*_*gbn 7

正如预期的那样。

标量 UDF 对优化器来说是一个黑匣子:无法使用索引,无法正确计算成本。

如果标量 UDF 具有表访问权限,那么您必须运行 CURSOR(在 SQL Server 中不好)来进行每行查找:它不是基于集合的操作

要修复它,请不要以这种方式使用 UDF。它可以写成一个 JOIN。没有提示或魔法

编辑:删除UDF。对于评级中的每一行,您的查询 Ableitungen。它是指数级的

我已经将过滤器移到 JOIN 中

With ratings as
(
    select
        ROW_NUMBER() Over (ORDER BY SEDate) lfd, 
        SEDate, 
        BBCode 
    from  PPV
        join BB on BBRefnr = SEBBRefnr
    where SEHORefnr = @HORefnr 
    and SEBBRefnr > 0
)
Select 
    l.SEDate,
    l.BBCode,
    A.Code,
    D.DTRefnr
from 
    ratings l
    left join 
    ratings r on l.lfd = r.lfd + 1
    JOIN 
    Ableitungen A ON A.Value = l.BBCode
    join
    DT d on A.code = d.DTCode and d.DTDTKRefnr = A.DTZ
where
    A.DTZ = @fDtz
    AND 
   (l.BBCode <> r.BBCode or r.BBCode is null)
order by
    SEDATE;
Run Code Online (Sandbox Code Playgroud)