我想提高具有多个联接和case语句的SQL视图的性能

问题描述 投票:1回答:1

在我看来,我有多个联接,联接之一是多对多关系。因此,当我加入该表时,记录数急剧增加,为了解决该问题,我使用了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

有什么建议可以改善我的查询性能。

sql-server sqlperformance
1个回答
0
投票

首先,最好以一种有组织的方式格式化代码。如果您在论坛上发布代码,不仅可以简化调试和维护过程,还可以得到更好的答案。我整理了一下以更好地解释我的答案并帮助其他人提供更好的答案。

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}
© www.soinside.com 2019 - 2024. All rights reserved.