Во-первых, обратите внимание, что ваш запрос, как есть, всегда будет возвращать 1
для соответствующих строк независимо от того, что. Как есть, вы можете изменить код на [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 или плана выполнения трудно помочь, но вот некоторые незначительные изменения плода. Для вашего первого CTE против [Period] вы хотите, чтобы индекс поддерживал этот запрос:
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)
Это не меняет ответ, но оставляя DISTINCT, может привести к тому, что оптимизатор будет без необходимости сортировать эти строки.
Наконец - у вас есть ТРИ коррелированных подзапроса , также известный как треугольное соединение . ЭТО ТО, ЧТО НАИБОЛЕЕ Дробление ВАША ДЕЯТЕЛЬНОСТЬ. Есть много способов реорганизовать этот код, но это может быть сложно. Простой способ улучшить производительность этих коррелированных подзапросов - превратить этот коррелированный подзапрос в индексированное представление, если это возможно. Логика представления c будет выглядеть следующим образом:
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}