我想知道是否有人可以帮助简化此程序 - 并提高性能......!
我们有关于拨款的数据。 “捐赠者”向“收件人”提供资金,我们希望在3个时间段内显示每位捐赠者的前15位收件人:CurrentYear-20,CurrentYear-10和CurrentYear。我们发布年度报告,显示每个捐赠者的World和GeoZone总数的百分比份额。
我已经“继承”了这个由我的前任之一编写的代码。在我们切换到使用视图之前,执行时间大约是15-30分钟。目前,它运行在四个小时(计划为服务器代理作业)之下!管理层不高兴。由于各种原因,必须继续使用该视图,并且目前具有来自20世纪50年代以后的数据的不到900,000行。我们目前为30个(大型)捐赠者运行此报告,每年增加更多。
为了帮助提高性能,我考虑过使用CTE或/使用SUM()OVER(Partition BY ...)或它们的组合,但我不知道如何去做。
有人能指出我正确的方向吗?
以下是流程:
正如您从上面所看到的,每个捐赠者的计算运行54次(18x3)!
这是代码(简化):
-- @LatestYear is passed as a parameter, hardcoded here for simplicity
DECLARE @LatestYear SMALLINT ,
@CurrentYear SMALLINT ,
@DonorID SMALLINT ,
@totalWorld NUMERIC(10, 2) ,
@LoopCounter TINYINT ,
@DonorName VARCHAR(100)
SELECT @latestyear = 2012
-- create a table to hold list of top 15 recipients for each donor and their 'share' of ODA.
DECLARE @Top15 TABLE
(
Country VARCHAR(100) ,
Percentage REAL
)
-- create a table to hold list of donors, ordered as they need to appear in the report.
DECLARE @PageOrder TABLE
(
DonorID SMALLINT ,
DonorName VARCHAR(100) ,
SortOrder SMALLINT IDENTITY(1, 1)
)
-- create a table to store the "focus" donor.
DECLARE @CurrentDonor TABLE ( DonorID SMALLINT )
INSERT INTO @PageOrder
SELECT DonorID ,
DonorName
FROM dbo.LookupDonor
ORDER BY DonorName;
-- cursor to loop through the donors in SortOrder
DECLARE DonorCursor CURSOR
FOR
SELECT DonorID ,
DonorName
FROM @PageOrder
ORDER BY DonorName;
OPEN DonorCursor
FETCH NEXT FROM DonorCursor INTO @DonorID, @DonorName
WHILE @@fetch_status = 0
BEGIN
INSERT INTO pubOutput
( XMLText )
SELECT @DonorName;
-- Populate the DonorID table
INSERT INTO @CurrentDonor
VALUES ( @DonorID )
/* The following loop is invoked 3 times. The first time through, the year will be 20 years before the latest year,
the second time through, 10 years before. The last time through the year will be the latest year.
*/
SET @LoopCounter = 1
WHILE @LoopCounter <= 3
BEGIN
SELECT @CurrentYear = CASE @LoopCounter
WHEN 1 THEN @LatestYear - 20
WHEN 2 THEN @LatestYear - 10
ELSE @LatestYear
END
-- calculate the world total for the current years (year,year-1) for all recipients
SELECT @totalWorld = SUM(Amount)
FROM dbo.vData2 d
INNER JOIN ( SELECT RecipientID
FROM dbo.RecipientGroup
WHERE GroupID = 160
) c ON d.RecipientID = c.RecipientID
INNER JOIN @CurrentDonor z ON d.DonorID = z.DonorID
WHERE d.year IN ( @CurrentYear - 1, @CurrentYear )
-- calculate the GeoZones total for the current years (year,year-1)
SELECT @totalGeoZones = SUM(Amount)
FROM dbo.vDac2a d
INNER JOIN ( SELECT RecipientID
FROM dbo.GeoZones
WHERE GeoZoneID = 100
) x ON d.RecipientID = x.RecipientID
INNER JOIN @CurrentDonor z ON d.DonorCode = z.DonorCode
WHERE d.year IN ( @CurrentYear - 1, @CurrentYear )
-- Find the top 15 recipients for the current donor
INSERT INTO @Top15
SELECT TOP 15
r.RecipientName ,
( ISNULL(SUM(Amount), 0) / @totalWorld ) * 100
FROM dbo.vData2 d
INNER JOIN dbo.LookupRecipient r ON r.RecipientID = d.RecipientID
INNER JOIN @CurrentDonor z ON d.DonorID = z.DonorID
WHERE d.year IN ( @CurrentYear - 1, @CurrentYear )
GROUP BY r.RecipientName
ORDER BY 2 DESC
-- Print the top 15 recipients and total
INSERT INTO pubOutput
(
XMLText
)
SELECT country + @Separator + CAST(percentage AS VARCHAR)
FROM @Top15
ORDER BY percentage DESC
INSERT INTO pubOutput
(
XMLText
)
SELECT @Heading1 + @Separator + CAST(SUM(Percentage) AS VARCHAR)
FROM @Top15
-- Breakdown by Regionas
-- Region1
IF @totalWorld IS NOT NULL
INSERT INTO pubOutput
(
XMLText
)
SELECT 'Region1' + @Separator
+ CAST(( ISNULL(SUM(Amount), 0) / @totalWorld ) * 100 AS VARCHAR)
FROM dbo.vData2 d
INNER JOIN ( SELECT RecipientID
FROM dbo.RecipientGroup
WHERE RegionID = 1
) c ON d.RecipientID = c.RecipientID
INNER JOIN @CurrentDonor z ON d.DonorID = z.DonorID
WHERE d.year IN ( @CurrentYear - 1, @CurrentYear )
ELSE -- force output of sub-total heading
INSERT INTO pubOutput
(
XMLText
)
SELECT @Heading2 + @Separator + '--'
-- Region2-8
/* similar syntax as Region1 above, for all Regions 2-8 */
-- Total Regions
INSERT INTO pubOutput
(
XMLText
)
SELECT @Heading2 + @Separator + CAST(@totalWorld AS VARCHAR)
-- Breakdown by GeoZones 1-7
-- GeoZone1
INSERT INTO pubOutput
(
XMLText
)
SELECT 'GeoZone1' + @Separator
+ CAST(( ISNULL(SUM(Amount), 0) / @totalGeoZones ) * 100 AS VARCHAR)
FROM dbo.vDac2a d
INNER JOIN ( SELECT RecipientID
FROM dbo.GeoZones
WHERE GeoZoneID = 1
) m ON d.RecipientID = m.RecipientID
INNER JOIN @CurrentDonor z ON d.DonorCode = z.DonorCode
WHERE d.year IN ( @CurrentYear - 1, @CurrentYear )
-- GeoZones2-8
/* similar syntax as GeoZone1 above for GeoZones 2-7 */
-- Total GeoZones - currently hard-coded as 100, due to minor rounding errors
INSERT INTO pubOutput
(
XMLText
)
SELECT @Heading3 + @Separator + '100'
SET @LoopCounter = @LoopCounter + 1
END -- year loop
-- Get the next donor from the cursor
FETCH NEXT FROM DonorCursor
INTO @DonorID, @DonorName
END
-- donorcursor
-- Cleanup
CLOSE DonorCursor
DEALLOCATE DonorCursor
非常感谢您提供的任何帮助。
答案 0 :(得分:3)
必须避免光标。您可以使用'while'而不是光标。但是考虑到查询的复杂性,请在此时保持光标。
要以其他方式提高效果,请检查以下查询的记录数:
我建议为上面的游标“outside”创建3个临时表,并在游标内部使用它们。
希望这有帮助!