好的,所以我阅读了很多有关表值函数和交叉应用的文章,这些文章比标量udf具有更好的性能。我想用两种方式编写函数,然后进行测试以查看哪种更好- 但我无法弄清楚应该使用/寻找的是哪种更好的选择。
我正在使用SQL Server2005。我尝试运行估计的执行计划,实际的执行计划并在数据库引擎优化顾问中分析查询,但我不知道它想告诉我什么。
使用showplan_all on / off似乎基于表的函数将使用更多的cpu 1.157e-06和8.3e-05,但是该表函数的子树总开销为0.000830157和0.01983356。
表值函数的查询成本似乎也比标量函数高。即使我认为这应该是更好的选择。
因此,尽管我想自己证明哪一种性能更好-我只是不确定这些工具中要寻找什么-因此,任何建议都将不胜感激!
我需要获取基于日历日期的学年值(基于数据库中设置的日期范围),因此函数内容如下-因此,无论我使用的是标量还是表。今年将纳入其他查询。
CREATE FUNCTION fn_AcademicYear ( -- Add the parameters for the function here @StartDate DateTime ) RETURNS @AcademicYear TABLE ( AcademicYear int ) AS BEGIN DECLARE @YearOffset int, @AcademicStartDate DateTime -- Lookup Academic Year Starting Date SELECT @AcademicStartDate = CONVERT(DateTime,[Value]) FROM dbo.SystemSetting WHERE [Key] = 'AcademicYear.StartDate' SET @YearOffset = DATEPART(YYYY,@StartDate) - DATEPART(YYYY,@AcademicStartDate); -- try setting academic looking start date to year of the date passed in SET @AcademicStartDate = DATEADD(YYYY, @YearOffset, @AcademicStartDate); IF @StartDate < @AcademicStartDate BEGIN SET @AcademicStartDate = DATEADD(YYYY, @YearOffset-1, @AcademicStartDate); END INSERT @AcademicYear SELECT YEAR(@AcademicStartDate) RETURN
谢谢!!
您可能没有看到期望的性能提升,因为表值函数是多功能的,而不是内联的。多功能TVF必须以与标量UDF相同的方式执行-每行一次-因此收益很小。
按照Itzik Ben-Gan在本文中的示例(讨论了串联TVF的好处),设置以下测试:
创建一个具有一百万行的数字表:
SET NOCOUNT ON; IF OBJECT_ID('dbo.T1') IS NOT NULL DROP TABLE T1; GO WITH L0 AS (SELECT 0 AS c UNION ALL SELECT 0), L1 AS (SELECT 0 AS c FROM L0 AS A CROSS JOIN L0 AS B), L2 AS (SELECT 0 AS c FROM L1 AS A CROSS JOIN L1 AS B), L3 AS (SELECT 0 AS c FROM L2 AS A CROSS JOIN L2 AS B), L4 AS (SELECT 0 AS c FROM L3 AS A CROSS JOIN L3 AS B), L5 AS (SELECT 0 AS c FROM L4 AS A CROSS JOIN L4 AS B), Nums AS (SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 0)) AS n FROM L5) SELECT n INTO dbo.T1 FROM Nums WHERE n <= 1000000;
使用以下代码运行一百万次TVF执行:
set statistics time on SELECT n,DATEADD(HOUR,n,'1900-01-01'),AY.AcademicYear FROM T1 CROSS APPLY dbo.fn_AcademicYear(DATEADD(HOUR,n,'1900-01-01')) AS AY set statistics time off
在我的系统上,这表明三个执行DBCC dropcleanbuffers之间平均要耗时83秒,每个执行之间要运行一次。
DBCC dropcleanbuffers
如果对标量值函数执行类似的测试,则应该对比较性能有一个更清晰的认识。
该测试还揭示了您的功能中似乎存在错误的地方。如果将AcademicYear.StartDate设置为“ 2010-09-01”,则输入“ 1900-01-01”返回的“学年”为1789,看来应该是1899。
AcademicYear.StartDate
为了获得最佳性能,您需要将TVF转换为嵌入式-我提出了以下建议,我相信可以纠正该错误:
CREATE FUNCTION fn_AcademicYear2 ( @StartDate DATETIME ) RETURNS TABLE AS RETURN ( -- Lookup Academic Year Starting Date WITH dtCTE AS ( SELECT CONVERT(DATETIME,[Value]) AS dt FROM dbo.SystemSetting WHERE [KEY] = 'AcademicYear.StartDate' ) SELECT CASE WHEN @StartDate >= DATEADD(YEAR,DATEDIFF(YEAR,dt,@StartDate),dt) THEN YEAR(@StartDate) ELSE YEAR(DATEADD(YEAR,DATEDIFF(YEAR,dt,@StartDate) - 1,dt)) END AS AcademicYear FROM dtCTE ) GO
在三轮运行中,平均耗时为8.9秒-快了将近十倍。
要考虑的另一件事是,除非您将TVF应用于多行,否则使用TVF所带来的性能优势将是微不足道的。如果一次使用一个值,除非有成千上万个并行执行的函数实例,否则您不会看到很多好处。