存在函数时的 TSQL 视图选择优化
Posted
技术标签:
【中文标题】存在函数时的 TSQL 视图选择优化【英文标题】:TSQL view select optimization when function is present 【发布时间】:2015-01-12 16:52:26 【问题描述】:我将这个简单的 SQL 作为 SSIS 任务的源:
Select * from budgetview
来源是:
CREATE VIEW [dbo].[BudgetView] AS
SELECT DISTINCT Country,
SDCO AS Company,
SDAN8 AS Customer,
SDLITM AS PrintableItemNumber,
dbo.fn_DateFromJulian(SDIVD) AS Date,
SDPQOR/100.0 AS Quantity,
SDAEXP/100.0 AS Value,
SDITWT/10000.0 AS Weight
FROM dbo.F553460
没有关于索引的建议,每件事似乎都经过优化。
函数fn_DateFromJulian
源码为:
CREATE FUNCTION [dbo].[fn_DateFromJulian]
(
@JulianDate numeric(6,0)
)
RETURNS date
AS
BEGIN
declare @resultdate date=dateadd(year,@JulianDate/1000,'1900-01-01')
set @resultdate=dateadd(day,@JulianDate%1000 -1,@resultdate)
return @resultdate
END
问题是我要等待大约 20 分钟才能让行进入 SSIS....
我在它开始前 20 分钟在那里等待
有什么建议可以找到罪魁祸首吗?
【问题讨论】:
标量是邪恶的 sqlblog.com/blogs/paul_white/archive/2012/09/05/… sqlblog.com/blogs/rob_farley/archive/2011/11/08/… 在我看来,理想的做法是将它变成一个内联表值函数,然后照样使用它。可能会为您节省一些处理时间。您可以做的另一件事是通过提示加快前 10k 的速度,但在这里似乎有问题sqlblog.com/blogs/rob_farley/archive/2011/02/17/… 你能帮我理解你的功能吗?查看wiki article,我应该能够将 2451545 传递给函数并返回 2001-01-1 但函数只接受 6 位数字 不是这样:110235 是 2010 年的第 235 天。我在我们的 ERP,JDEdwards 中使用它。看到这个:***.com/questions/1171208/… 没有该功能的相同查询的性能如何? 所以如果我的回答并不能真正解决问题,那就不要这样标记。让我们看看发生了什么。独角兽积分很棒,但让我们解决问题。在管理 Stupdi 中,按 Ctrl-M 以生成实际的查询计划,然后针对视图运行 SELECT 语句。右键单击计划,另存为 e4rthdog.sqlplan,然后发布到... sqlperformance.com 或 github,这样比我更聪明的人可以看到查询中发生了什么。另外,这个包中是否还有其他数据流,或者只是你展示的这个? 【参考方案1】:我的假设是在视图上花费的时间是通过计算儒略日期值来消耗的。在没有看到实际查询计划的情况下,根据下面的文章,这似乎是一个合理的猜测。
将原始函数重写为下面的表值函数(我只是将您的代码混合在一起,可能有改进的机会)
CREATE FUNCTION dbo.fn_DateFromJulianTVF
(
@JulianDate numeric(6,0)
)
RETURNS TABLE AS
RETURN
(
SELECT dateadd(day,@JulianDate%1000 -1,dateadd(year,@JulianDate/1000,CAST('1900-01-01' AS date))) AS JDEDate
)
用法是
CREATE VIEW [dbo].[BudgetView] AS
SELECT DISTINCT Country,
SDCO AS Company,
SDAN8 AS Customer,
SDLITM AS PrintableItemNumber,
J.JDEDate AS [Date],
SDPQOR/100.0 AS Quantity,
SDAEXP/100.0 AS Value,
SDITWT/10000.0 AS Weight
FROM dbo.F553460 AS T
CROSS APPLY
dbo.fn_DateFromJulianTVF(T.SDIVD) AS J
标量值函数,闻起来像代码重用,性能像重复使用的一次性尿布
https://sql.kiwi/2012/09/compute-scalars-expressions-and-execution-plan-performance.html http://blogs.lobsterpot.com.au/2011/11/08/when-is-a-sql-function-not-a-function/【讨论】:
【参考方案2】:只是检查一下,但我是否正确理解 T.SDIVD
的每个唯一值将只有一个函数的唯一结果值?换句话说,没有两个不同的T.SDIVD
会从函数返回相同的值?
在这种情况下(恕我直言),您首先扫描整个表,为每条记录计算 f(SDIVD) 值,然后通过聚合 (DISTINCT) 发送整个结果集。
由于 MSSQL 中的函数远非最佳,我建议通过扭转事件链并像这样来限制它们的使用:
CREATE VIEW [dbo].[BudgetView] AS
SELECT /* DISTINCT */
Country,
Company,
Customer,
PrintableItemNumber,
dbo.fn_DateFromJulian(SDIVD) AS Date,
Quantity,
Value,
Weight
FROM (
SELECT DISTINCT Country,
SDCO AS Company,
SDAN8 AS Customer,
SDLITM AS PrintableItemNumber,
SDIVD,
SDPQOR/100.0 AS Quantity,
SDAEXP/100.0 AS Value,
SDITWT/10000.0 AS Weight
FROM dbo.F553460 ) dist_F553460
)
如果您有很多双记录,这应该会提高性能,如果您只有很少的记录,则不会有太大的不同(如果有的话)。如果你知道你根本没有双打,你应该首先摆脱DISTINCT
,因为这是导致延迟的原因!
不管怎样,关于函数,你可以添加以下技巧:
CREATE FUNCTION [dbo].[fn_DateFromJulian]
(
@JulianDate numeric(6,0)
)
RETURNS date
WITH SCHEMABINDING
AS
BEGIN
declare @resultdate date=dateadd(year,@JulianDate/1000,'1900-01-01')
set @resultdate=dateadd(day,@JulianDate%1000 -1,@resultdate)
return @resultdate
END
WITH SCHEMABINDING
会导致一些内部优化,使其执行速度稍快,YMMV。它有一些限制,但在这里它会很好地工作。
编辑:删除了“外部”DISTINCT,因为它(可能,参见我的第一个假设)不需要。
【讨论】:
以上是关于存在函数时的 TSQL 视图选择优化的主要内容,如果未能解决你的问题,请参考以下文章