Loops 优化具有While循环和交叉应用的T-SQL查询
试图优化为计算1200万滚动收入而编写的T-SQL查询。主要问题是我需要跨6个维度进行计算,得出45000多个组合,每个组合都需要自己的滚动收入。每种组合大约需要5分钟来计算。由于业务的性质,合并将随着时间的推移而增长 所有操作都需要作为子查询完成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
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