Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/231.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Loops 优化具有While循环和交叉应用的T-SQL查询_Loops_Tsql_Query Optimization_Cross Apply - Fatal编程技术网

Loops 优化具有While循环和交叉应用的T-SQL查询

Loops 优化具有While循环和交叉应用的T-SQL查询,loops,tsql,query-optimization,cross-apply,Loops,Tsql,Query Optimization,Cross Apply,试图优化为计算1200万滚动收入而编写的T-SQL查询。主要问题是我需要跨6个维度进行计算,得出45000多个组合,每个组合都需要自己的滚动收入。每种组合大约需要5分钟来计算。由于业务的性质,合并将随着时间的推移而增长 所有操作都需要作为子查询完成 DECLARE @NUMBER INT DECLARE @ROWCOUNT INT = 0 DECLARE @REFERENCE VARCHAR(50) SET @NUMBER = (SELECT MAX(r.Row) FR

试图优化为计算1200万滚动收入而编写的T-SQL查询。主要问题是我需要跨6个维度进行计算,得出45000多个组合,每个组合都需要自己的滚动收入。每种组合大约需要5分钟来计算。由于业务的性质,合并将随着时间的推移而增长

所有操作都需要作为子查询完成

DECLARE @NUMBER INT
DECLARE @ROWCOUNT INT = 0
DECLARE @REFERENCE VARCHAR(50)

SET @NUMBER =
    (SELECT MAX(r.Row)
        FROM (
            SELECT 
                a.GroupKey
                ,a.LevelKey
                ,a.StateKey
                ,a.ProductKey
                ,a.OptionKey
                ,a.LocKey
                ,ROW_NUMBER() OVER(ORDER BY a.GroupKey ASC) AS Row
            FROM Members AS a
            GROUP BY 
                a.GroupKey
                ,a.LevelKey
                ,a.StateKey
                ,a.ProductKey
                ,a.OptionKey
                ,a.LocKey ) r)
BEGIN
DECLARE @OUTPUT TABLE (
        RefKey VARCHAR(50)
        ,DateID INT
        ,GroupKey INT
        ,LevelKey INT
        ,StateKey INT
        ,ProductKey INT
        ,OptionKey INT
        ,LocKey INT
        ,Revenue DECIMAL(28,9)
        ,Rolling12Months DECIMAL(28,9)
         )

WHILE @ROWCOUNT < @NUMBER

BEGIN
SET @REFERENCE =
    (SELECT r.RefKey
        FROM (
            SELECT
                r.Refkey
                ,r.Row
                FROM (          
                    SELECT 
                        CONCAT( a.GroupKey, '-',a.LevelKey, '-',a.StateKey, '-',a.ProductKey, '-',a.OptionKey, '-',a.LocKey) AS RefKey
                        ,ROW_NUMBER() OVER(ORDER BY a.GroupKey ASC) AS Row
                    FROM Members AS a
                    GROUP BY 
                        a.GroupKey
                        ,a.LevelKey
                        ,a.StateKey
                        ,a.ProductKey
                        ,a.OptionKey
                        ,a.LocKey ) r) r WHERE r.Row = @ROWCOUNT + 1 )

INSERT INTO @OUTPUT         
SELECT
    a.RefKey
    ,a.DateID
    ,a.GroupKey
    ,a.LevelKey
    ,a.StateKey
    ,a.ProductKey
    ,a.OptionKey
    ,a.LocKey
    ,a.Revenue
    ,Rolling12Months=a.Revenue + b.RM 

FROM (

        SELECT
            CONVERT(datetime,STR(CI.DateID)) AS Date
            ,CI.DateID
            ,CI.GroupKey
            ,CI.LevelKey
            ,CI.StateKey
            ,CI.ProductKey
            ,CI.OptionKey
            ,CI.LocKey
            ,CONCAT( CI.GroupKey, '-',CI.LevelKey, '-',CI.StateKey, '-',CI.ProductKey, '-',CI.OptionKey, '-',CI.LocKey) AS RefKey
            ,SUM(CI.Revenue) AS Revenue
        FROM Members AS CI
        WHERE CONCAT( CI.GroupKey, '-',CI.LevelKey, '-',CI.StateKey, '-',CI.ProductKey, '-',CI.OptionKey, '-',CI.LocKey) = @REFERENCE
        GROUP BY CI.DateID,CI.StateKey,CI.GroupKey  ,CI.LevelKey,CI.StateKey,CI.ProductKey,CI.OptionKey ,CI.LocKey

        ) a
CROSS APPLY
( 
    SELECT RM=SUM(b.Revenue)
    FROM
    (
        SELECT TOP 11 b.Date, b.Revenue 
        FROM (

                SELECT
                    CONVERT(datetime,STR(CI.DateID)) AS Date
                    ,CI.DateID
                    ,CI.GroupKey
                    ,CI.LevelKey
                    ,CI.StateKey
                    ,CI.ProductKey
                    ,CI.OptionKey
                    ,CI.LocKey
                    ,CONCAT( CI.GroupKey, '-',CI.LevelKey, '-',CI.StateKey, '-',CI.ProductKey, '-',CI.OptionKey, '-',CI.LocKey) AS RefKey
                    ,SUM(CI.Revenue) AS Revenue
                FROM Members AS CI
                WHERE CONCAT( CI.GroupKey, '-',CI.LevelKey, '-',CI.StateKey, '-',CI.ProductKey, '-',CI.OptionKey, '-',CI.LocKey) = @REFERENCE
                GROUP BY CI.DateID,CI.StateKey,CI.GroupKey  ,CI.LevelKey,CI.StateKey,CI.ProductKey,CI.OptionKey ,CI.LocKey

        ) b
        WHERE b.Date < a.Date AND b.RefKey = a.RefKey
        ORDER BY b.Date DESC
    ) b
) b

    SET @ROWCOUNT = @ROWCOUNT + 1
    END
    SELECT * FROM @OUTPUT
END

我对你的代码有几点看法

尝试使用表变量的临时表intead-一个表变量可能有多行的性能问题

通过连接许多列来动态生成一个键看起来很简洁,但是这样做会停止使用任何索引或统计信息。如果需要,可以在单独的列上进行连接,而不是在它们上使用函数

您可以将日期列转换为日期时间,然后加入其中-同样,您应该使用未更改的列进行比较

你没有解释你的日期栏-你计算你的滚动12个月仅仅是因为前11个最后日期小于当前日期加上当前日期

,Rolling12Months=a.Revenue + b.RM 

您是否考虑过一个月内可能会有多个日期?在这种情况下,你将得到不到12个月的数据在你的总和。或者ii,对于按列分组的某些组合,有几个月没有数据,在这种情况下,您的总和将持续更多个月

但是,如果我们只需要按列对组的每个组合的最后12个日期求和,那么我只需要这样做:

/* When using many rows a temp table often performs better than a table variable */
DROP TABLE IF EXISTS #OUTPUT

CREATE TABLE #OUTPUT  (
        RefKey VARCHAR(50)
        ,DateID INT
        ,GroupKey INT
        ,LevelKey INT
        ,StateKey INT
        ,ProductKey INT
        ,OptionKey INT
        ,LocKey INT
        ,Revenue DECIMAL(28,9)
        ,Rolling12Months DECIMAL(28,9)
         )

/* I am doing a simple select and using the analytical sum, which can be set up with a window */
SELECT
    CONCAT(CI.GroupKey, '-', CI.LevelKey, '-', CI.StateKey, '-', CI.ProductKey, '-', CI.OptionKey, '-', CI.LocKey) RefKey
   ,CI.DateID
   ,CI.GroupKey
   ,CI.LevelKey
   ,CI.StateKey
   ,CI.ProductKey
   ,CI.OptionKey
   ,CI.LocKey
   ,CI.Revenue
   ,SUM(CI.Revenue) OVER (PARTITION BY CI.GroupKey, CI.LevelKey, CI.StateKey
    , CI.ProductKey, CI.OptionKey, CI.LocKey ORDER BY CI.DateID 
    ROWS BETWEEN 11 PRECEDING AND CURRENT ROW) Rolling12Months
FROM Members AS CI
GROUP BY
    CI.DateID
   ,CI.GroupKey
   ,CI.LevelKey
   ,CI.StateKey
   ,CI.ProductKey
   ,CI.OptionKey
   ,CI.LocKey

SELECT
    *
FROM #OUTPUT

这应该会给你同样的结果,但是我不会使用它,因为它只返回最后11个日期加上每个列组合的当前日期的总和!但是如果它在您的原始代码中工作,那么它应该复制您的结果

您应该做的第一件事可能是摆脱循环。SQL不能很好地处理循环,如果可能的话,应该找到一种基于集合的方法来替代它。我没有读你发布的代码,但这是SQL中的一条金科玉律。谢谢Zohar。我不确定是否有任何方法不使用循环来聚集属性。
/* When using many rows a temp table often performs better than a table variable */
DROP TABLE IF EXISTS #OUTPUT

CREATE TABLE #OUTPUT  (
        RefKey VARCHAR(50)
        ,DateID INT
        ,GroupKey INT
        ,LevelKey INT
        ,StateKey INT
        ,ProductKey INT
        ,OptionKey INT
        ,LocKey INT
        ,Revenue DECIMAL(28,9)
        ,Rolling12Months DECIMAL(28,9)
         )

/* I am doing a simple select and using the analytical sum, which can be set up with a window */
SELECT
    CONCAT(CI.GroupKey, '-', CI.LevelKey, '-', CI.StateKey, '-', CI.ProductKey, '-', CI.OptionKey, '-', CI.LocKey) RefKey
   ,CI.DateID
   ,CI.GroupKey
   ,CI.LevelKey
   ,CI.StateKey
   ,CI.ProductKey
   ,CI.OptionKey
   ,CI.LocKey
   ,CI.Revenue
   ,SUM(CI.Revenue) OVER (PARTITION BY CI.GroupKey, CI.LevelKey, CI.StateKey
    , CI.ProductKey, CI.OptionKey, CI.LocKey ORDER BY CI.DateID 
    ROWS BETWEEN 11 PRECEDING AND CURRENT ROW) Rolling12Months
FROM Members AS CI
GROUP BY
    CI.DateID
   ,CI.GroupKey
   ,CI.LevelKey
   ,CI.StateKey
   ,CI.ProductKey
   ,CI.OptionKey
   ,CI.LocKey

SELECT
    *
FROM #OUTPUT