在我看来,我有多个联接,联接之一是多对多关系。因此,当我加入该表时,记录数急剧增加,为了解决该问题,我使用了case语句,该语句有助于过滤不需要的记录。但是,对不需要的记录进行的整个过滤使创建查询的速度非常慢。
这是查询的基本结构。
> WITH PeriodCTE AS ( SELECT PeriodId, StartTime, Name,
> LEAD(StartTime, 1) OVER (ORDER BY TimePeriodId) [EndTime] FROM
> Period WHERE Active = 1 )
>
> Select * From (Select
> Id,
> Name,
> Period,
> CASE WHEN CAST(B.OpenTimestamp AS TIME) BETWEEN '00:00:00.0000000'
> AND (SELECT MIN(P.Starttime) FROM Period P
> INNER JOIN DepartmentPeriod DP ON P.PeriodId = DP.PeriodId
> WHERE DepartmentId = B.DepartmentId)
> AND CTE.StartTime = (SELECT MAX(T.Starttime) FROM
> Period P
> INNER JOIN
> DepartmentPeriod DP ON
> P.PeriodId = DP.PeriodId
> WHERE DepartmentId = B.DepartmentId)
> Then 1
> When CAST(B.OpenTimestamp AS TIME) BETWEEN CTE.StartTime
> AND '23:59:59.9999999' AND CTE.StartTime =
> (SELECT MAX(P.Starttime) FROM Period P
> INNER JOIN DepartmentPeriod DP ON
> P.TimePeriodId = DP.TimePeriodId
> WHERE DepartmentId = C.DepartmentId)
> Then 1
> WHEN CAST(B.OpenTimestamp AS TIME) BETWEEN CTE.StartTime AND CTE.EndTime
> THEN 1
> WHEN DP.DepartmentId NOT IN (SELECT DISTINCT(DepartmentId) FROM DepartmentPeriod)
> THEN 1
> WHEN CTE.StartTime IS NULL
> THEN 1
> END AS [Flag]
> From Bill AS B
> LEFT OUTER JOIN Department AS D ON B.DepartmentId = D.DepartmentId
> LEFT OUTER JOIN DepartmentPeriod AS DP ON D.DepartmentId = D.DepartmentId
> LEFT OUTER JOIN PeriodCTE AS CTE ON P.PeriodId = DP.PeriodId ) AS X
> Where Flag = 1
有什么建议可以改善我的查询性能。
首先,最好以一种有组织的方式格式化代码。如果您在论坛上发布代码,不仅可以简化调试和维护过程,还可以得到更好的答案。我整理了一下以更好地解释我的答案并帮助其他人提供更好的答案。
WITH PeriodCTE AS
(
SELECT -- 1. You want an index to handle the sort for TimePeriodId
PeriodId, StartTime, Name, LEAD(StartTime, 1) OVER (ORDER BY TimePeriodId) AS [EndTime]
FROM [Period]
WHERE Active = 1
) -- INDEX: CREATE NONCLUSTERED INDEX <name> ON [Period](TimePeriodId) WHERE Active = 1;
SELECT *
FROM
(
SELECT
Id,
Name,
Period,
[Flag] =
CASE
WHEN CAST(B.OpenTimestamp AS TIME) BETWEEN '00:00:00.0000000' AND (
SELECT MIN(P.Starttime)
FROM Period AS P
JOIN DepartmentPeriod AS DP
ON P.PeriodId = DP.PeriodId
WHERE DepartmentId = B.DepartmentId)
AND CTE.StartTime = (
SELECT MAX(T.Starttime)
FROM Period AS P
JOIN DepartmentPeriod AS DP
ON P.PeriodId = DP.PeriodId
WHERE DepartmentId = B.DepartmentId)
THEN 1
WHEN CAST(B.OpenTimestamp AS TIME) BETWEEN CTE.StartTime AND '23:59:59.9999999'
AND CTE.StartTime = (
SELECT MAX(P.Starttime)
FROM Period AS P
JOIN DepartmentPeriod AS DP
ON P.TimePeriodId = DP.TimePeriodId
WHERE DepartmentId = C.DepartmentId)
THEN 1
WHEN CAST(B.OpenTimestamp AS TIME) BETWEEN CTE.StartTime AND CTE.EndTime
THEN 1
WHEN DP.DepartmentId NOT IN (SELECT /*DISTINCT*/DepartmentId FROM DepartmentPeriod) -- 2. You don't need DISTINCT Here, it causes a needless sort
THEN 1
WHEN CTE.StartTime IS NULL
THEN 1
END
FROM Bill AS B
LEFT JOIN Department AS D ON B.DepartmentId = D.DepartmentId
LEFT JOIN DepartmentPeriod AS DP ON D.DepartmentId = D.DepartmentId
LEFT JOIN PeriodCTE AS CTE ON P.PeriodId = DP.PeriodId
) AS x
WHERE Flag = 1;
没有DDL或执行计划,很难帮上忙,但这是一些低调的成果。对于您针对[Period]的第一个CTE,您希望索引支持该查询
CREATE NONCLUSTERED INDEX <name> ON [Period](TimePeriodId, StartTime)
INCLUDE (PeriodId, Name)
WHERE Active = 1;
或...
CREATE NONCLUSTERED INDEX <name> ON [Period](TimePeriodId)
INCLUDE (PeriodId, StartTime, Name)
WHERE Active = 1;
接下来,您可以在此子查询中丢失DISTINCT:
WHEN DP.DepartmentId NOT IN (SELECT /*DISTINCT*/DepartmentId FROM DepartmentPeriod)
这不会改变答案,但是可能导致优化器不必要地对那些行进行排序。
最后-您有三个correlated subqueries,也称为triangle join。这就是您最可能CRUSHING的表现。您可以采用多种方法来重构该代码,但是它可能很复杂。一种提高这些相关子查询性能的简单方法是,如果可能的话,将相关子查询转换为索引视图。视图逻辑如下所示:
CREATE {viewname} WITH SCHEMABINDING AS
SELECT {Unique key or composite key}, P.Starttime
FROM dbo.[Period] AS P
JOIN dbo.DepartmentPeriod AS DP
ON P.PeriodId = DP.PeriodId
WHERE DepartmentId = B.DepartmentId;
索引将是值的一些独特组合:
CREATE UNIQUE CLUSTERED INDEX {indexname} ON {yourview}(Starttime, {unique key(s)});
然后,在查询中找到该代码存在的三个位置:
SELECT MIN(V.Starttime)
FROM dbo.[Period] AS P
JOIN dbo.DepartmentPeriod AS DP
ON P.PeriodId = DP.PeriodId
WHERE DepartmentId = B.DepartmentId
并将其更改为...
SELECT MIN(V.Starttime)
FROM {your new indexed view}