Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 我想提高具有多个联接和case语句的SQL视图的性能_Sql Server_Sqlperformance - Fatal编程技术网

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

Sql server 我想提高具有多个联接和case语句的SQL视图的性能,sql-server,sqlperformance,Sql Server,Sqlperformance,在我看来,我有多个连接,其中一个连接在多对多关系上。因此,当我加入这个表时,记录的数量急剧增加,为了解决这个问题,我使用了case语句来帮助过滤不需要的记录。但是,对不需要的记录进行过滤的过程非常缓慢 下面是我的查询的基本结构 > WITH PeriodCTE AS ( SELECT PeriodId, StartTime, Name, > LEAD(StartTime, 1) OVER (ORDER BY Ti

在我看来,我有多个连接,其中一个连接在多对多关系上。因此,当我加入这个表时,记录的数量急剧增加,为了解决这个问题,我使用了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

是否有改进我的查询性能的建议。

首先,请注意,对于符合条件的行,您的查询将始终返回
1
。按原样,您可以将代码更改为
[flag]=1
。也就是说,我暂时不考虑这个问题

> 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;
这不会改变答案,但留下不同的结果可能会导致优化器对这些行进行不必要的排序

最后,你有三个,也称为a。这是最有可能压倒你表现的。有很多方法可以重构代码,但可能很复杂。提高这些相关子查询性能的一个简单方法是,如果可能,将相关子查询转换为索引视图。视图逻辑如下所示:

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}

“我不能将我的查询或执行计划作为客户项目发布。”那么,您对我们有什么期望?如果你只告诉一个机械师“它跑得很慢”,不告诉他诸如品牌和型号之类的基本信息,也不告诉他为什么它跑得很慢,你希望他能告诉你如何修理你的车吗?只提供这些细节。。。。向itTbh扔硬件,我会先调整查询的性能,@uberbloke,然后再向它扔硬件。确保它是SARGable的。我希望OP没有在
WHERE
中使用
CASE
表达式。当然,我们完全不可能知道。首先,你需要确定你想要什么。如果你想要一个具体的方法,就指出它。你能描述一下你想用
CASE
语句实现什么吗?瓶颈很可能存在,而且在您的实现中似乎存在一些冗余。
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}