我有一个性能问题,我想我可能已经解决了,但我需要帮助理解为什么可能的解决方案可以改进SQL Server的行为,最重要的是,它是否可靠(例如,不太可能随着数据的增长和变化或者简单的代码更改而突然变慢)。我也开放给更好的解决方案。这里有很多背景,请耐心等待。我一直在对SQL Server 2008 R2进行开发和测试。通过CTE嵌套时的性能
我正在研究检查数据的某些条件的系统,并根据这些条件在后台自动执行不同的操作。这些相同的条件用于向用户显示信息,例如dbo.Cases
中给定条目何时发生下一个动作,或者为什么不执行动作。
有一个视图收集这些条件的所有数据,并使用CASE WHEN
语句选择条件作为位标志。这些列可以由UI使用,也可以通过自动轮询过程在WHERE
子句中使用。
这些条件通常以相同的方式使用,因此希望避免在任何地方重复它们。创建了“总览列”,用于检查其他条件:IsSubmittable和IsAutomaticallySubmittable。
这里的基本观点是什么样子:
CREATE VIEW dbo.vwDataExtended
AS
WITH
Data AS (
SELECT Cases.CNR,
Cases.CNRLink,
IsActiveCompany =
CASE
WHEN Companies.IsActive IS NULL THEN CAST(0 as BIT)
ELSE Companies.IsActive
END,
IsOpen =
CASE
WHEN Cases.SCODE = 'O' THEN CAST(1 as BIT)
ELSE CAST(0 as BIT)
END,
IsReopened =
CASE
WHEN Cases.SCODE = 'R' THEN CAST(1 as BIT)
ELSE CAST(0 as BIT)
END,
IsCompanyClassCode =
CASE
WHEN CompanyClassCode.CompanyClassCodeID IS NOT NULL THEN CAST(1 as BIT)
ELSE CAST(0 as BIT)
END
--several other conditions
FROM dbo.Cases with (nolock)
LEFT JOIN dbo.Companies with (nolock)
ON Companies.CompanyCode = Cases.CompanyCode
AND Companies.CustomerLevelTypeCode = 'CUSTOMER'
LEFT JOIN dbo.ClassCodes with (nolock)
ON ClassCodes.ClassCode = Cases.ClassCode
--identify enabled company class codes
LEFT JOIN dbo.ISO_Search_CompanyClassCode CompanyClassCode with (nolock)
ON CompanyClassCode.CompanyCode = Cases.CompanyCode
AND CompanyClassCode.ClassCode = ClassCodes.ClassCode
AND CompanyClassCode.EnableOn <= GETDATE()
--lots of other joins
) --end CTE Data
AddIsSubmittable as (
select *,
IsSubmittable = case when
IsActiveCompany = 1
and IsCompanyClassCode = 1
--7 other similar conditions
then cast(1 as bit)
else cast(0 as bit)
end
from Data
), --end CTE AddIsSubmittable
AddIsAutomaticallySubmittable as (
select *,
IsAutomaticallySubmittable = case when
IsSubmittable = 1
and (IsOpen = 1 OR IsReopened = 1)
--2 other similar conditions
then cast(1 as bit)
else cast(0 as bit)
end
from AddIsSubmittable
) --end CTE AddIsAutomaticallySubmittable
select CNR,
CNRLINK,
IsActiveCompany,
IsOpen,
IsReopened,
IsCompanyClassCode,
IsISOSubmittable,
IsAutomaticallySubmittable
from AddIsAutomaticallySubmittable
这里是一个自动投票过程如何使用它的一个例子:
DECLARE CURSOR_NEW CURSOR LOCAL FAST_FORWARD FOR
SELECT DISTINCT
d.CNR
FROM dbo.vwDataExtended d with(nolock)
WHERE d.IsAutomaticallySubmittable = 1
AND --process-specific conditions
从概念上讲,这样的设计是好的,因为它确保了UI代码始终与自动化流程的代码同步。然而,它要求视图非常高效,因为其中一些过程相当频繁地运行(主要是间隔10分钟),并且在dbo.Cases
中有近300万行。
通过上述实现,轮询过程的查询运行非常缓慢。查看执行计划,将会收集dbo.Cases
中每行的汇总列的所有数据,然后在靠近结尾处对其进行过滤。每个轮询过程特有的其他条件处理不当。
,我发现的潜在解决方案是从vwDataExtended
去除卷起列,并添加每一个与他们的WHERE
子句中的条件的单独视图,如这样的:
create view dbo.vwDataExtended_IsSubmittable
as
select CNR,
CNRLINK,
IsActiveCompany,
IsOpen,
IsReopened,
IsCompanyClassCode
from dbo.vwDataExtended with(nolock)
where IsActiveCompany = 1
and IsCompanyClassCode = 1
--7 other similar conditions
go
create view dbo.vwDataExtended_IsAutomaticallySubmittable
as
select CNR,
CNRLINK,
IsActiveCompany,
IsOpen,
IsReopened,
IsCompanyClassCode
from dbo.vwDataExtended_IsSubmittable with(nolock)
where (IsOpen = 1 OR IsReopened = 1)
--2 other similar conditions
然后,轮询过程的查询被这样修改:
DECLARE CURSOR_NEW CURSOR LOCAL FAST_FORWARD FOR
SELECT DISTINCT
d.CNR
FROM dbo.vwDataExtended_IsAutomaticallySubmittable d with(nolock)
WHERE --process-specific conditions
此实现生成的显着改善的执行计划,显示出SQL Server使用vwDataExtended
的的内容语句作为查找和扫描的谓词,因此极大地限制了它检查的行数。
但是,此实现确实带来了成本:用户界面必须LEFT JOIN
带有dbo.vwDataExtended
的新视图才能实现等同于原始dbo.vwDataExtended
的功能。这是非常低效的,并且使得一些相当繁忙的执行计划,尽管执行时间可能仍然可以接受(如果只是很少)。
回到原来的问题:为什么这会提高SQL Server的行为呢?有没有可以用来解释差异的文档?有没有可能替代这种设计,不涉及到所有地方的逻辑重复?
IIRC CTE一旦被访问就不存储,每次使用时都需要重新评估。在这个例子中,'Data' CTE被引用3次。如果您将该核心CTE设置为写入表变量,并使用它,则只需处理一次。 – Malk
CTE在链中使用(一个选择从另一个直到视图的选择在底部),所以它只能使用一次。我从未在执行计划中看到任何暗示多种用途正在发生的事情。 – Taudris
如果这是真的,这个查询将在两列中返回相同的guid:';与cte1作为(选择id = newid()),cte2作为(从cte1选择ID)从cte1选择cte1.id,cte2.id,cte2 ' – Malk