存在函数时的 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 视图选择优化的主要内容,如果未能解决你的问题,请参考以下文章

连接使用表值函数创建的表时优化 TSQL

TSQL函数和Check约束以确保表中存在父值

解析 TSQL 选择查询并返回列表达式

TSQL和SQL区别?

TSQL UDF 根据第一个函数参数的值返回具有不同列数的表

TSQL - 更好的 INT 转换函数