Sql server 用户定义函数和存储过程之间的性能差异
如果一条语句在数据库上执行简单的select返回行,那么使用函数和过程实现它之间是否存在性能差异?Sql server 用户定义函数和存储过程之间的性能差异,sql-server,performance,stored-procedures,function,Sql Server,Performance,Stored Procedures,Function,如果一条语句在数据库上执行简单的select返回行,那么使用函数和过程实现它之间是否存在性能差异? 我知道最好使用函数,但它真的更快吗?我认为您应该更关心如何使用此函数,而不是速度。UDF可以出现在select语句的其他位置,甚至可以用作联接等的“表”。您不能从存储过程中“选择”或在其中一个存储过程上联接 然而,UDF是为每一行调用的,所以我会小心地使用它。这让我有一次遇到了真正的麻烦。这一点我永远不会忘记。只要SQL看到开始或结束,系统就无法简化内容 因此,真正的区别在于函数的结果可以用于外部
我知道最好使用函数,但它真的更快吗?我认为您应该更关心如何使用此函数,而不是速度。UDF可以出现在select语句的其他位置,甚至可以用作联接等的“表”。您不能从存储过程中“选择”或在其中一个存储过程上联接
然而,UDF是为每一行调用的,所以我会小心地使用它。这让我有一次遇到了真正的麻烦。这一点我永远不会忘记。只要SQL看到开始或结束,系统就无法简化内容 因此,真正的区别在于函数的结果可以用于外部查询、连接、忽略某些列等等
实际上,您最好使用视图或内联表值函数,这样SQL就可以简化它,只做您感兴趣的部分。更多信息,请在我的博客上查找我关于“开始和结束的危险”的帖子 在函数中运行查询与在过程中运行查询在速度上没有区别 存储过程在聚合结果时遇到问题,无法与其他存储过程组合。onyl解决方案非常麻烦,因为它需要使用
INSERT。。。执行…
,然后使用结果表
函数具有高度可组合性的优点,因为表值函数可以放置在任何需要表表达式的位置(FROM、JOIN、APPLY、IN等)。但是函数在函数中允许什么和不允许什么方面有一些非常严重的限制,这正是因为它们可以出现在查询中的任何位置
苹果和橙子的关系也是如此。决策的驱动力不是性能,而是需求。一般来说,返回数据集的任何内容都应该是视图或表值函数。任何操纵数据的操作都必须是一个过程 并非所有UDF都对性能有害。
普遍存在一种误解,即自定义项对性能有负面影响。总而言之,这根本不是事实。事实上,内联表值UDF实际上是宏——优化器能够很好地重写涉及它们的查询并优化它们。然而,标量UDF通常非常慢。我将提供一个简短的例子
先决条件
以下是创建和填充表的脚本:
CREATE TABLE States(Code CHAR(2), [Name] VARCHAR(40), CONSTRAINT PK_States PRIMARY KEY(Code))
GO
INSERT States(Code, [Name]) VALUES('IL', 'Illinois')
INSERT States(Code, [Name]) VALUES('WI', 'Wisconsin')
INSERT States(Code, [Name]) VALUES('IA', 'Iowa')
INSERT States(Code, [Name]) VALUES('IN', 'Indiana')
INSERT States(Code, [Name]) VALUES('MI', 'Michigan')
GO
CREATE TABLE Observations(ID INT NOT NULL, StateCode CHAR(2), CONSTRAINT PK_Observations PRIMARY KEY(ID))
GO
SET NOCOUNT ON
DECLARE @i INT
SET @i=0
WHILE @i<100000 BEGIN
SET @i = @i + 1
INSERT Observations(ID, StateCode)
SELECT @i, CASE WHEN @i % 5 = 0 THEN 'IL'
WHEN @i % 5 = 1 THEN 'IA'
WHEN @i % 5 = 2 THEN 'WI'
WHEN @i % 5 = 3 THEN 'IA'
WHEN @i % 5 = 4 THEN 'MI'
END
END
GO
并将其与涉及内联表值UDF的查询进行比较:
CREATE FUNCTION dbo.GetStateName_Inline(@StateCode CHAR(2))
RETURNS TABLE
AS
RETURN(SELECT [Name] FROM dbo.States WHERE Code = @StateCode);
GO
SELECT ID, (SELECT [name] FROM dbo.GetStateName_Inline(StateCode)) AS StateName
INTO dbo.ObservationsWithStateNames_Inline
FROM dbo.Observations
它的执行计划和执行成本都是相同的——优化器将其重写为外部连接。不要低估优化器的威力
涉及标量UDF的查询要慢得多。
下面是一个标量UDF:
CREATE FUNCTION dbo.GetStateName(@StateCode CHAR(2))
RETURNS VARCHAR(40)
AS
BEGIN
DECLARE @ret VARCHAR(40)
SET @ret = (SELECT [Name] FROM dbo.States WHERE Code = @StateCode)
RETURN @ret
END
GO
显然,使用此UDF的查询提供了相同的结果,但它有不同的执行计划,而且速度非常慢:
/*
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 3 ms.
Table 'Worktable'. Scan count 1, logical reads 202930, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Observations'. Scan count 1, logical reads 188, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 11890 ms, elapsed time = 38585 ms.
*/
如您所见,优化器可以重写和优化涉及内联表值UDF的查询。另一方面,涉及标量UDF的查询不会被优化器重写——最后一个查询的执行每行包含一个函数调用,这非常慢
简单SELECT语句将受到您正在查询的表上任何索引的最大影响 优化器位于所选数据库引擎的核心,负责就如何运行传入的查询做出重要决策 在编写查询时,值得花时间学习索引、优化程序、主键等。挑选一些数据库引擎;SQL Server与mySQL不同,Oracle与两者都不同。还有很多,每一个都在某种程度上有所不同 存储过程可以非常快,因为它们是预编译的。优化人员不必每次都制定执行计划。存储过程将以表格形式返回结果 函数可以是标量(返回单个结果)或返回表格数据 编写效率低下的函数和存储过程是很可能的。重要的是问问自己是否需要该功能以及如何维护它
如果您还没有拥有Joe Celko的书,那么现在可能是投资的时候了。我第一次尝试使用内联表值函数(TVF),实际花费了66到76%(1.147到1.2秒vs.0.683秒)更长的时间(相对于存储过程(SP))!?!这是100次迭代的平均值,每次迭代89行。我的SP只是在执行标准的
设置nocount on
,然后是一个复杂的(但仍然是单一的)select
语句(带有5个内部连接
,以及2个外部连接
(其中一个内部连接
具有嵌入select
的表达式)(它本身有一个where
表达式(带有一个嵌入式select
+internaljoin
))和一个groupby
和orderby
,有5列和一个count
)。调用者是一个插入到一个临时表中的对象(带有标识
列,但没有键或索引)-语句。即使没有SP正在执行的order by
命令,内联TVF也花费了66%的时间。当我将其添加回时(在select
调用内联TVF,因为内联TVF中不能有order by
),花费的时间更长(76%)???/p>UDF如果被视为是过程性的,那就不好了。你应该考虑一个内联表值函数,它会简化成一个视图。这是真正的性能提高的地方。你确定每一行都调用了所有的UDFs吗?这对于标量UDFS来说是正确的。
/*
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 3 ms.
Table 'Worktable'. Scan count 1, logical reads 202930, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Observations'. Scan count 1, logical reads 188, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 11890 ms, elapsed time = 38585 ms.
*/