udf vs直接sql性能

Equ*_*xor 5 sql optimization sql-server-2005 user-defined-functions

使用MSSQL 2005

我今天在一个where语句中用Scalar UDF来玩,看看与调用和io差异等相关的一些成本.

我从2个基本表开始.客户有100万行.和购买有100,000.两者都有一个自动标识列作为主键.没有定义其他索引.

DBCC FREEPROCCACHE
DBCC DROPCLEANBUFFERS

SET STATISTICS IO ON
    SELECT * FROM Customer C 
    INNER JOIN Purchases P on C.[IDENTITY] = P.CustomerID
    WHERE P.Amount > 1000
SET STATISTICS IO OFF
Run Code Online (Sandbox Code Playgroud)

这将返回IO的统计信息

Table 'Customer'. Scan count 0, logical reads 3295, physical reads 1, read-ahead reads 32, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Purchases'. Scan count 1, logical reads 373, physical reads 1, read-ahead reads 370, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Run Code Online (Sandbox Code Playgroud)

因此,只是为了看到标量UDF的影响,我只是将P.Amount> 1000移动到UDF.功能如下:

CREATE FUNCTION [dbo].[HighValuePurchase]
(
    @value int
)
RETURNS bit
AS
BEGIN
    DECLARE @highValue bit
    SET @highValue = '0'

    IF @value > 1000
    BEGIN
        SET @highValue = '1'
    END
    RETURN @highValue
END
Run Code Online (Sandbox Code Playgroud)

然后我运行以下查询:

DBCC FREEPROCCACHE
DBCC DROPCLEANBUFFERS

SET STATISTICS IO ON      
    SELECT * FROM Customer C 
    INNER JOIN Purchases P on C.[IDENTITY] = P.CustomerID
    WHERE dbo.HighValuePurchase(P.Amount) = '1'
SET STATISTICS IO OFF
Run Code Online (Sandbox Code Playgroud)

我原以为这会变得更糟.此查询返回以下IO统计信息:

Table 'Purchases'. Scan count 1, logical reads 373, physical reads 1, read-ahead reads 370, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Customer'. Scan count 1, logical reads 35, physical reads 3, read-ahead reads 472, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Run Code Online (Sandbox Code Playgroud)

这也比> 1000查询返回得更快.返回相同的行时,调用UDF的行的顺序由C. [IDENTITY]自动排序,其中另一个查询显示未排序.这可能是由于组合在执行计划中的完成方式.计划大纲如下.

非UDF的执行计划显示针对购买的聚集索引扫描以及针对嵌套联接的客户的聚集索引搜索.

UDF版本的执行计划显示购买的聚集索引扫描,然后是过滤器,然后是排序.客户上有一个聚集索引扫描.然后将结果合并到Merge Join中.

我确定这与缺少索引等有关,但我不确定为什么这些结果是他们的方式.我经历过UDF的运行速度很慢,每个人都说使用它们通常是一个坏主意,这就是我把这个测试放在一起的原因.我目前无法解释为什么UDF版本似乎更好.

Ben*_*Ben 2

  • 如果你想加入,Purchases.CustomerID你应该在上面添加一个索引。
  • 如果您经常查询值范围,您也应该在其上放置索引。

事实上,您要求 SQL Server 在两个糟糕的计划之间进行选择。

SQL Server 可以粗略地猜测查询将涵盖多少次购买> 1000,并据此选择一个计划。

然而,它无法猜测 UDF 查询将覆盖多少个,因此可能会选择不同的计划。因为它是无知的,所以它可能比其他计划更好或更差,这取决于它的猜测有多好。

您可以看到生成的计划,它会告诉您每个计划中的估计行数以及实际数量。这些估计数字说明了每种情况下计划的选择。