我有一个性能问题,我认为我可能已经解决了,但我需要帮助理解为什么可能的解决方案可以改善SQL Server的行为,最重要的是,它是否可靠(例如,不太可能突然变慢)随着数据的增长和变化,或者简单的代码更改)。我也完全接受更好的解决方案。这里有很多背景,所以请耐心等待。我一直在针对SQL Server 2008 R2进行开发和测试。
我正在研究检查数据某些条件的系统,并根据这些条件,在后台自动执行不同的操作。这些相同的条件用于向用户显示信息,例如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
的{{1}}语句的内容作为查找和扫描的谓词,从而极大地限制了它检查的行数。
此实施确实带来了成本:用户必须CASE WHEN
LEFT JOIN
新视图才能获得与原始dbo.vwDataExtended
相同的功能。这是非常低效的,并且使得一些相当繁忙的执行计划,尽管执行时间可能仍然可以接受(如果只是勉强)。
回到最初的问题:为什么这会如此改善SQL Server的行为?有没有可用于解释差异的文档?是否有一种替代这种设计的方法并不涉及到整个地方复制逻辑?
答案 0 :(得分:0)
我避免使用CTE,除非需要递归,或者CTE可以在最终查询中重用。这两个条件都不适用,因此我根本不会使用CTE。
您正在使用CTE来添加&#34;基于一些先前的计算,但还有其他两种技术。
/* a simple "derived table" (or "inline view") */
select ..., comp.IsActiveCompany
from (
select *, IsActiveCompany =
case when Companies.IsActive IS NULL THEN CAST(0 as BIT)
ELSE Companies.IsActive END
) comp
/* using the apply operator for the calculation */
.
select ..., ca1.IsActiveCompany
from dbo.Companies
OUTER APPLY (
SELECT
CASE
WHEN Companies.IsActive IS NULL THEN CAST(0 as BIT)
ELSE Companies.IsActive
END
) as ca1 (IsActiveCompany)
这两种技术都使您能够通过引用给定的列别名来引用某些先前的计算。
我很想尝试将OUTER APPLY用于那些你想通过别名引用的计算,然后我也会把整个视图当作一个派生表来消除对CTE的所有需要。显然,其中一些是对最适合什么的猜测,因此继续使用执行计划进一步调查。
e.g。
select CNR,
CNRLINK,
IsActiveCompany,
IsOpen,
IsReopened,
IsCompanyClassCode,
IsISOSubmittable,
IsAutomaticallySubmittable
from (
SELECT Cases.CNR,
Cases.CNRLink,
CA1.IsActiveCompany,
IsSubmittable = case when
CA1.IsActiveCompany = 1
and IsCompanyClassCode = 1
--7 other similar conditions
then cast(1 as bit)
else cast(0 as bit)
end
-- lots of stuff missing to put back in
FROM dbo.Cases with (nolock)
LEFT JOIN dbo.Companies with (nolock)
ON Companies.CompanyCode = Cases.CompanyCode
AND Companies.CustomerLevelTypeCode = 'CUSTOMER'
OUTER APPLY (
SELECT
CASE
WHEN Companies.IsActive IS NULL THEN CAST(0 as BIT)
ELSE Companies.IsActive
END
) as ca1 (IsActiveCompany)
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
) AS derived