Sql server 我想提高具有多个联接和case语句的SQL视图的性能
在我看来,我有多个连接,其中一个连接在多对多关系上。因此,当我加入这个表时,记录的数量急剧增加,为了解决这个问题,我使用了case语句来帮助过滤不需要的记录。但是,对不需要的记录进行过滤的过程非常缓慢 下面是我的查询的基本结构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
> 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}