使用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
返回
的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.
所以只是为了看到标量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
然后我运行了以下查询:
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
我原以为这会变得更糟。此查询返回以下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.
这也比>更快地返回1000查询。返回相同的行时,调用UDF的行的顺序由C. [IDENTITY]自动排序,其中另一个查询显示未排序。这可能是由于组合在执行计划中的完成方式。计划大纲如下。
非UDF的执行计划显示了针对采购的聚集索引扫描以及针对嵌套联接的客户的聚集索引搜索。
UDF版本的执行计划显示了针对购买的聚集索引扫描,然后是过滤器,然后是排序。客户上有一个聚集索引扫描。然后将结果合并到合并连接中。
我确定这与缺少索引等有关,但我不确定为什么这些结果是他们的方式。我经历过UDF的运行速度很慢,每个人都说使用它们通常是一个坏主意,这就是我把这个测试放在一起的原因。我目前无法解释为什么UDF版本似乎更好。
答案 0 :(得分:1)
Purchases.CustomerID
,则应在其上加上索引。 因为它是你要求SQL服务器在两个糟糕的计划之间进行选择。
SQL Server可以大致猜测> 1000
查询将涵盖的购买次数,并将基于此选择一个计划。
然而,它无法猜测UDF查询将覆盖多少,因此可能会选择不同的计划。因为它是无知的,它可能比其他计划更好或更糟,取决于它的猜测有多好。
您可以看到生成的计划,它会告诉您每个计划中的估计行数以及实际数量。这些估计数字是每种情况下计划选择的原因。