我有一个报告调度系统,其中每个报告都有一组参数,动态提示用户,然后报告作为SQL代理作业执行,将输出保存到存档数据库以供日后查看。
这一切都运行良好,但用户想要更改其中一个参数。他们曾经指定一个成员和位置(这定义了一个客户),但现在他们希望能够指定多个成员和位置。例如,根据他们在英国和NI(两个地点)的运营情况,他们可能希望看到2012年Comet销量排名前50位的产品。
我的报告过去需要几秒钟才能运行,但是这次更改现在需要大约8分钟!
当报告调度程序获取报告请求时,它会将成员/位置列表存储在表中,然后将其链接到报告中。这似乎是事情出错的地方。
以下是报告的开头(这足以解决性能问题):
CREATE FUNCTION [Report].[GetTopSaleQuantityPackagingTonnage] (
@ReportHandle UNIQUEIDENTIFIER,
@DSFYear INT,
@DateFrom DATETIME,
@DateTo DATETIME,
@LevelId INT,
@MaterialId INT, --Base Material
@TopN INT,
@TopNType VARCHAR(50),
@WeightSource VARCHAR(50),
@ImportExportFlag VARCHAR(1) = NULL)
RETURNS TABLE
AS
RETURN
WITH MemberList AS (
SELECT
MemberId,
LocationId
FROM
Report.ReportMemberLocation
WHERE
ReportHandle = @ReportHandle),
ProductList AS (
SELECT DISTINCT
pw.ProductId,
pw.WeightSource,
m.MaterialId
FROM
PackagingWeightDerivedBase pw
INNER JOIN Material m ON m.ExtendedMaterialId = pw.ExtendedMaterialId
WHERE
EXISTS (SELECT * FROM MemberList ml WHERE ml.MemberId = pw.MemberId)
AND ISNULL(@LevelId, pw.LevelId) = pw.LevelId
AND ISNULL(@MaterialId, m.MaterialId) = m.MaterialId)
SELECT * FROM ProductList;
如果我将单个过滤器添加到ProductList CTE,例如
AND MemberId = 163
然后它又回到了几秒而不是几分钟。 MemberId = 163是我运行报告的成员,在这种情况下,他们只有一个位置。
我最初尝试过这个:
ProductList AS (
SELECT DISTINCT
pw.ProductId,
pw.WeightSource,
m.MaterialId
FROM
MemberList ml
INNER JOIN PackagingWeightDerivedBase pw ON pw.MemberId = ml.MemberId
INNER JOIN Material m ON m.ExtendedMaterialId = pw.ExtendedMaterialId
WHERE
ISNULL(@LevelId, pw.LevelId) = pw.LevelId
AND ISNULL(@MaterialId, m.MaterialId) = m.MaterialId)
......但那更慢,我离开了30分钟,它从未完成。此外,这在任何情况下都不是严格正确的,因为它会为成员/位置过滤器中的每一行带回重复的结果。
所以我知道问题在于优化器没有明智地处理这个问题。基本上它有:
Report.ReportMemberLocation - 1 row
Material - about 50 rows
PackagingWeightDerivedBase - millions of rows
...当它发现它们属于不同的成员时,它必须拔出数百万行数据,以便将99.99%的数据扔掉。
我是否可以通过其他方式表达此查询以强制优化器首先按成员列表过滤权重表?或者也许是一些我不知道的提示?