【发布时间】:2014-02-05 17:38:52
【问题描述】:
某个查询(在 Dynamics CRM 2011 数据库上)的性能非常糟糕。由于它是一个规范化的数据模型,但需要对此数据(SSRS 报告)进行扁平化视图,因此我使用 SELECT TOP (1) 子查询做了很多 (12) LEFT OUTER JOIN,例如:
LEFT JOIN Filterednew_rates FRates ON FRates.new_ratesid =
(SELECT TOP (1)
FRR.new_ratesid
FROM Filterednew_rates FRR
WHERE
FRR.new_contractid = FContract.contractid
AND FRR.statuscode <> 803270000 -- NOT Obsolete
ORDER BY FRR.new_startdate DESC
)
这适用于少量结果行(例如 3 行需要 10 秒),但我已经在大约 100 个预期结果行上运行了 45 分钟(源数据量相同,只是 WHERE 不同条款)。因此,我开始寻找“强制”SQL Server 每行运行子查询的方法(从逻辑上讲,这对我来说是线性扩展的)。
然后我阅读了The power of T-SQL's APPLY operator 并设法将上面的内容更改为
OUTER APPLY (
SELECT TOP (1)
FRR.*
FROM Filterednew_rates FRR
WHERE
FRR.new_contractid = FContract.contractid
AND FRR.statuscode <> 803270000 -- NOT Obsolete
ORDER BY FRR.new_startdate DESC
) AS FRates
这使得执行时间与结果记录的数量大致呈线性关系(100 行约 3:30 分钟,3 行约 6 秒)。不知何故,这使得 SQL Server 决定更好地更改查询执行计划!
在 SQL 中是否有任何其他方法可以在不借助集成/分析服务的情况下“扁平化”规范化数据模型?
编辑:
感谢@Aaron 和@BAReese 的输入。我将尝试应用 PIVOT/UNPIVOT 和窗口函数并报告查询性能差异。
根据大众的要求,查询的很大一部分。我尝试将查询“匿名化”一下,因此实际的查询属性更具描述性。
OUTER APPLY (
SELECT TOP (1)
FCO.*
FROM Filterednew_contractoption FCO
WHERE
FCO.new_contractid = FContract.contractid
AND FCO.new_included = 1 -- Is Included
AND FCO.new_optionidname = 'SomeOption1'
) AS FOptionSomeOption1
OUTER APPLY (
SELECT TOP (1)
FCO.*
FROM Filterednew_contractoption FCO
WHERE
FCO.new_contractid = FContract.contractid
AND FCO.new_included = 1 -- Is Included
AND FCO.new_optionidname = 'SomeOption2'
) AS FOptionSomeOption2
OUTER APPLY (
SELECT TOP (1)
FCD.*
FROM FilteredContractDetail FCD
JOIN FilteredProduct FProd ON FCD.productid = FProd.productid
WHERE
FContract.contractid = FCD.contractid
AND FCD.new_included = 1 -- Is Included
AND FProd.productnumber IN ('COLDEL1', 'COLDEL2', 'COLDEL3', 'COLDEL4')
) AS FColDelContractDetail
LEFT JOIN FilteredProduct FColDelProduct ON FColDelContractDetail.productid = FColDelProduct.productid
OUTER APPLY (
SELECT TOP (1)
FCO.*
FROM Filterednew_contractoption FCO
JOIN Filterednew_contractdetail_new_contractoptions FCD_CO ON FCO.new_contractoptionid = FCD_CO.new_contractoptionid
WHERE
FCD_CO.contractdetailid = FColDelContractDetail.contractdetailid
AND FCO.new_included = 1 -- Is Included
AND FCO.new_optionidname LIKE 'Input1'
) AS FColDelInput1Option
OUTER APPLY (
SELECT TOP (1)
FCO.*
FROM Filterednew_contractoption FCO
JOIN Filterednew_contractdetail_new_contractoptions FCD_CO ON FCO.new_contractoptionid = FCD_CO.new_contractoptionid
WHERE
FCD_CO.contractdetailid = FColDelContractDetail.contractdetailid
AND FCO.new_included = 1 -- Is Included
AND FCO.new_optionidname LIKE 'Input2'
) AS FColDelInput2Option
OUTER APPLY (
SELECT TOP (1)
FCO.*
FROM Filterednew_contractoption FCO
JOIN Filterednew_contractdetail_new_contractoptions FCD_CO ON FCO.new_contractoptionid = FCD_CO.new_contractoptionid
WHERE
FCD_CO.contractdetailid = FColDelContractDetail.contractdetailid
AND FCO.new_included = 1 -- Is Included
AND FCO.new_optionidname LIKE 'Input3'
) AS FColDelInput3Option
OUTER APPLY (
SELECT TOP (1)
FCP.*
FROM Filterednew_price FCP
WHERE FCP.new_contractid = FContract.contractid
AND FCP.statuscode <> 803270000 -- NOT Obsolete
ORDER BY FCP.new_validfrom DESC
) AS FPrice
OUTER APPLY (
SELECT TOP (1)
FCFR.*
FROM Filterednew_contractforecastresult FCFR
WHERE FCFR.new_contractid = FContract.contractid
ORDER BY FCFR.createdon DESC
) AS FForecastResult
【问题讨论】:
-
您是否考虑过 PIVOT/UNPIVOT 而不是 12 个外连接?此外,当我们不知道原始数据是什么样子、输出应该是什么样子等时,我们很难考虑替代方案。您只向我们展示了一个外连接,其他 11 个过滤条件有何不同?
-
+1 从表面上看,这似乎是一种奇怪的数据处理方式
标签: sql sql-server tsql reporting-services sql-server-2008-r2