每行执行多个标量函数的最有效方法

时间:2017-02-14 14:41:48

标签: sql sql-server-2008 tsql

我正在努力加快查询速度。我需要每行运行2个标量值函数,并将几列作为参数。

这些函数根据运行总计进行计算,并且在SQL Server 2008中没有简单的方法(我很清楚)。

我的查询的CTE部分获取了我需要计算的所有行,并且需要为每个级别单独运行。客户与商店之间的关系是一对多的,但您无法根据其每个商店计算在客户端级别(Store =' All')进行计算。

我的第一次尝试是在' SELECT'中使用函数调用。声明,但我读到他们会同步运行,这是没有必要的。下面的尝试试图将计算转换为表值,以尝试异步运行它们。

我想知道OUTER APPLY是否可行,或者是否有更有效的方法来解决这个问题。

如果需要更多信息,请与我们联系!

DECLARE @StartTime INT
SET @StartTime=20170101

DECLARE @EndTime INT
SET @EndTime=20170131


;WITH IsAvgStores AS (
    SELECT
        COALESCE(ParentClient,'All') AS ParentClient,
        COALESCE(Client,'All') AS Client,
        COALESCE(Store,'All') AS Store,
        MAX(Answer_Threshold),
        MAX(SL_Threshold)
    FROM 
        client_hierarchy
    WHERE 
        GETDATE() BETWEEN EFF_BEGIN_DATE AND EFF_END_DATE 
    GROUP BY ROLLUP(ParentClient,Client,Store)
)
SELECT I.ParentClient,I.Client,I.Store
    ,SL.isAvg_SL_String
    ,A.isAvg_ASA_String
INTO #isAvgTemp
FROM IsAvgStores I
    OUTER APPLY ( SELECT dbo.isAvg_S_B(ParentClient,Client,Store,Answer_Threshold,@StartTime,@EndTime) AS isAvg_SL_String 
                ) SL
    OUTER APPLY ( SELECT dbo.isAvg_A_B(ParentClient,Client,Store,SL_Threshold,@StartTime,@EndTime)     AS isAvg_ASA_String
                ) A
WHERE ParentClient<>'All'

SELECT * 
FROM #isAvgTemp

2 个答案:

答案 0 :(得分:5)

评论太长了。

对于标量值函数,outer apply是多余的。你可以这样做:

SELECT I.ParentClient, I.Client, I.Store,
      dbo.isAvg_S_B(ParentClient, Client, Store,A nswer_Threshold, @StartTime, @EndTime) AS isAvg_SL_String,
      dbo.isAvg_A_B(ParentClient, Client, Store, SL_Threshold, @StartTime, @EndTime) as isAvg_ASA_String
INTO #isAvgTemp
FROM IsAvgStores I
WHERE ParentClient <> 'All';

这对性能完全没有影响;它只是简化了查询。

为了提高性能,您有三种选择:

  • 重写代码以删除功能;使用outer apply作为运行总和。
  • 重写代码以删除功能;使用递归CTE来计算运行总和。
  • 重写代码以删除功能;使用游标来计算运行金额。

这些都不是最佳选择。如果个别群体很小,前两个可能会有效。第三种可能是这种情况下的最佳选择 - 请注意我非常厌恶游标。

或者,您最好的选择:升级到更高版本的SQL Server并使用SQL Server 2012 +中提供的累积和函数。

答案 1 :(得分:0)

另一种选择是尝试将标量函数转换为返回标量值的inline table-valued functions(ITVF)。这可能适用于您的方案,也可能不适用。根据我的经验,complex scalar functions generally perform poorly in SQL Server on larger result sets因为他们运行RBAR。使用ITVF,SQL Server能够内联查询以优化查询计划。

您的查询看起来类似于您尝试使用OUTER APPLY完成的内容:

SELECT I.ParentClient, I.Client, I.Store, SL.isAvg_SL_String A.isAvg_ASA_String
INTO #isAvgTemp
FROM IsAvgStores I
OUTER APPLY dbo.isAvg_S_B(ParentClient,Client,Store,Answer_Threshold,@StartTime,@EndTime)SL
OUTER APPLY dbo.isAvg_A_B(ParentClient,Client,Store,SL_Threshold,@StartTime,@EndTime) A
WHERE ParentClient <> 'All';

不同之处在于您的函数将返回一个包含单个结果的表:

CREATE FUNCTION dbo.isAvg_S_B (...)
RETURNS TABLE
AS
(
    SELECT ... AS 'isAvg_SL_String'
    FROM ...
);

同样,这可能会或可能不会提高您的特定情况下的性能,但我认为值得一提以防止重复代码。

相关问题