标量用户定义函数内 T-Sql 查询的性能
Performance of T-Sql query inside scalar user-defined function
我最近查看了一位同事的 SQL 服务器函数(在 SQL Server 2008
中),他在其中将 T-SQL
查询放入标量用户定义函数中。然后在查询的 Select 子句中使用该函数。
我认为在函数内部进行查询会产生糟糕的性能,因为我假设每一行 returned 都必须 运行 查询 - 即使该查询已优化为查询指数.
这是我所说的一个例子:
create table [PERSON] (
ID int primary key,
FIRSTNAME NVARCHAR(100),
MIDDLENAME NVARCHAR(100) null,
LASTNAME NVARCHAR(100))
GO
INSERT INTO PERSON (ID, FIRSTNAME, MIDDLENAME, LASTNAME)
VALUES (1, 'BOB', 'M', 'BLUE')
INSERT INTO PERSON (ID, FIRSTNAME, MIDDLENAME, LASTNAME)
VALUES (2, 'VALERIE', 'J', 'GREEN')
INSERT INTO PERSON (ID, FIRSTNAME, MIDDLENAME, LASTNAME)
VALUES (3, 'SIMON', 'D', 'RED')
INSERT INTO PERSON (ID, FIRSTNAME, MIDDLENAME, LASTNAME)
VALUES (4, 'LIONEL', 'W', 'BROWN')
GO
-- Scalar Function with T-SQL
CREATE FUNCTION dbo.fn_FormatNameFromId(
@pnPersonId int
)
Returns nvarchar(300)
AS
Begin
return (Select FIRSTNAME + ' ' + MIDDLENAME + ' ' + LASTNAME
From PERSON
Where ID = @pnPersonId)
End
go
-- Scalar Function without T-SQL
CREATE FUNCTION dbo.fn_FormatNameFromValues(
@psFirstName nvarchar(100),
@psMiddleName nvarchar(100),
@psLastName nvarchar(100)
)
Returns nvarchar(300)
AS
Begin
return (@psFirstName + ' ' + @psMiddleName + ' ' + @psLastName)
End
go
-- T-SQL within function
select dbo.fn_FormatNameFromId(ID)
from PERSON
-- Pass values directly
select dbo.fn_FormatNameFromValues(FIRSTNAME, MIDDLENAME, LASTNAME)
FROM PERSON
比较这两个选择的执行计划时,我发现这两个函数的 cpu 或内存性能没有差异。即使将测试扩展到 return 数万行,也没有性能差异。
任何人都可以解释 fn_FormatNameFromId
如何或为什么不会造成性能损失吗?
就性能而言,就我个人而言,我不是 t-sql 函数的忠实粉丝,所以我通常在 SQL 服务器中尽可能避免使用它们,
是的,我知道用户函数在编程中有很多优点。
直接回答你的问题
When comparing the execution plans for both these selects, I could
find no difference in the cpu or memory performance of the two
functions
我通常不会根据 subtree/cpu 成本比较执行计划来检查哪个计划更好,哪个更差,因为这些成本仅基于估计值。
Can anybody explain how or why the fn_FormatNameFromId is not causing
a loss of performance?
因为您仍在处理少量数据,所以我测试了您的两个功能,如下所示:
declare @a int=1
Declare @name varchar(50)
while (@a<99999)
Begin
-- T-SQL within function
select @name = dbo.fn_FormatNameFromId(ID)
from PERSON
SET @a =@a+1
END
13 秒后在我的本地系统上 运行
declare @a int=1
Declare @name varchar(50)
while (@a<99999)
Begin
-- Pass values directly
select @name =dbo.fn_FormatNameFromValues(FIRSTNAME, MIDDLENAME, LASTNAME)
FROM PERSON
SET @a =@a+1
END
9 秒后在我的本地系统上 运行
啊啊我说过我不是函数的忠实粉丝所以 运行 没有函数
declare @a int=1
Declare @name varchar(50)
while (@a<99999)
Begin
-- Pass values directly
Select @name= FIRSTNAME + ' ' + MIDDLENAME + ' ' + LASTNAME
From PERSON
SET @a =@a+1
END
在我的系统中 运行 在 2 秒内完成
请阅读下面的文章
T-SQL User-Defined Functions: the good, the bad, and the ugly
你可以看到Estimated Execution Plan
和Actual Execution Plan
之间的区别
您个人希望 select dbo.fn_FormatNameFromId(ID)
from PERSON
像屏幕截图所示那样工作:
对查询 1(聚集索引扫描)中的每一行执行查询 2(聚集索引查找 - 或按 ID 进行子查询搜索)
但是 sql-server 看到了这些查询的整体模式并将实际查询计划优化为只有一个简单的 Clustered Index Scan
正如 sql-server 在你的第二个查询中所做的那样 select dbo.fn_FormatNameFromValues(FIRSTNAME, MIDDLENAME, LASTNAME)
FROM PERSON
.
我最近查看了一位同事的 SQL 服务器函数(在 SQL Server 2008
中),他在其中将 T-SQL
查询放入标量用户定义函数中。然后在查询的 Select 子句中使用该函数。
我认为在函数内部进行查询会产生糟糕的性能,因为我假设每一行 returned 都必须 运行 查询 - 即使该查询已优化为查询指数.
这是我所说的一个例子:
create table [PERSON] (
ID int primary key,
FIRSTNAME NVARCHAR(100),
MIDDLENAME NVARCHAR(100) null,
LASTNAME NVARCHAR(100))
GO
INSERT INTO PERSON (ID, FIRSTNAME, MIDDLENAME, LASTNAME)
VALUES (1, 'BOB', 'M', 'BLUE')
INSERT INTO PERSON (ID, FIRSTNAME, MIDDLENAME, LASTNAME)
VALUES (2, 'VALERIE', 'J', 'GREEN')
INSERT INTO PERSON (ID, FIRSTNAME, MIDDLENAME, LASTNAME)
VALUES (3, 'SIMON', 'D', 'RED')
INSERT INTO PERSON (ID, FIRSTNAME, MIDDLENAME, LASTNAME)
VALUES (4, 'LIONEL', 'W', 'BROWN')
GO
-- Scalar Function with T-SQL
CREATE FUNCTION dbo.fn_FormatNameFromId(
@pnPersonId int
)
Returns nvarchar(300)
AS
Begin
return (Select FIRSTNAME + ' ' + MIDDLENAME + ' ' + LASTNAME
From PERSON
Where ID = @pnPersonId)
End
go
-- Scalar Function without T-SQL
CREATE FUNCTION dbo.fn_FormatNameFromValues(
@psFirstName nvarchar(100),
@psMiddleName nvarchar(100),
@psLastName nvarchar(100)
)
Returns nvarchar(300)
AS
Begin
return (@psFirstName + ' ' + @psMiddleName + ' ' + @psLastName)
End
go
-- T-SQL within function
select dbo.fn_FormatNameFromId(ID)
from PERSON
-- Pass values directly
select dbo.fn_FormatNameFromValues(FIRSTNAME, MIDDLENAME, LASTNAME)
FROM PERSON
比较这两个选择的执行计划时,我发现这两个函数的 cpu 或内存性能没有差异。即使将测试扩展到 return 数万行,也没有性能差异。
任何人都可以解释 fn_FormatNameFromId
如何或为什么不会造成性能损失吗?
就性能而言,就我个人而言,我不是 t-sql 函数的忠实粉丝,所以我通常在 SQL 服务器中尽可能避免使用它们,
是的,我知道用户函数在编程中有很多优点。
直接回答你的问题
When comparing the execution plans for both these selects, I could find no difference in the cpu or memory performance of the two functions
我通常不会根据 subtree/cpu 成本比较执行计划来检查哪个计划更好,哪个更差,因为这些成本仅基于估计值。
Can anybody explain how or why the fn_FormatNameFromId is not causing a loss of performance?
因为您仍在处理少量数据,所以我测试了您的两个功能,如下所示:
declare @a int=1
Declare @name varchar(50)
while (@a<99999)
Begin
-- T-SQL within function
select @name = dbo.fn_FormatNameFromId(ID)
from PERSON
SET @a =@a+1
END
13 秒后在我的本地系统上 运行
declare @a int=1
Declare @name varchar(50)
while (@a<99999)
Begin
-- Pass values directly
select @name =dbo.fn_FormatNameFromValues(FIRSTNAME, MIDDLENAME, LASTNAME)
FROM PERSON
SET @a =@a+1
END
9 秒后在我的本地系统上 运行
啊啊我说过我不是函数的忠实粉丝所以 运行 没有函数
declare @a int=1
Declare @name varchar(50)
while (@a<99999)
Begin
-- Pass values directly
Select @name= FIRSTNAME + ' ' + MIDDLENAME + ' ' + LASTNAME
From PERSON
SET @a =@a+1
END
在我的系统中 运行 在 2 秒内完成
请阅读下面的文章
T-SQL User-Defined Functions: the good, the bad, and the ugly
你可以看到Estimated Execution Plan
和Actual Execution Plan
您个人希望 select dbo.fn_FormatNameFromId(ID)
from PERSON
像屏幕截图所示那样工作:
对查询 1(聚集索引扫描)中的每一行执行查询 2(聚集索引查找 - 或按 ID 进行子查询搜索)
但是 sql-server 看到了这些查询的整体模式并将实际查询计划优化为只有一个简单的 Clustered Index Scan
正如 sql-server 在你的第二个查询中所做的那样 select dbo.fn_FormatNameFromValues(FIRSTNAME, MIDDLENAME, LASTNAME)
FROM PERSON
.