2016-12-19 5 views
5

У меня есть следующий запрос, и он оказался очень дорогостоящим и занимает 6-8 секунд для выполнения. Рассматривая план выполнения, стоимость составляет 79% при операции SORT. Могу ли я получить какое-либо улучшение здесь?Головка бутылки на операции СОРТ

IMG

SELECT 
     A.StageName, 
     C.Month, 
     C.MonthName as Label, 
     C.Year, 
     isnull(A.Average,0) as Data 
    FROM 
    ( 
     SELECT 
      S.StageName, 
      MONTH(TimeIn) as MonthNumber, 
      DATENAME(MONTH,TimeIn) as Month, 
      YEAR(TimeIn) as Year, 
      ISNULL(AVG(DATEDIFF(mi,TimeIn,isnull(TimeOut,@TodayDate))),0) as Average 
     FROM 
      VisitMovement VM 
     INNER JOIN Stage S on 
      VM.StageID = S.StageID 
     WHERE 
      (VM.TimeIn >= @StartDate AND 
      VM.TimeIn < DATEADD (d,1,@EndDate)) AND 
      (VM.TimeOut < DATEADD (d,1,@EndDate) OR VM.TimeOut IS NULL) 
     GROUP BY 
      S.StageNumber, 
      S.StageName, 
      MONTH(TimeIn), 
      DATENAME(MONTH,TimeIn), 
      YEAR(TimeIn) 
    ) A 
    RIGHT JOIN (select distinct Month,MonthName,Year from Calendar WHERE DATE >= @StartDate AND DATE < DATEADD (d,1,@EndDate)) C on 
     A.MonthNumber = C.Month and 
     A.Month = C.MonthName and 
     A.Year = C.Year 
    GROUP BY 
     A.StageName, 
     C.Month, 
     C.MonthName, 
     C.Year, 
     A.Average 
    ORDER BY 
     CASE WHEN @Ordering = 'asc' THEN C.Year   END ASC, 
     CASE WHEN @Ordering = 'asc' THEN C.Month   END ASC, 
     CASE WHEN @Ordering = 'asc' THEN A.StageName  END ASC, 
     CASE WHEN @Ordering = 'desc' THEN C.Year   END DESC, 
     CASE WHEN @Ordering = 'desc' THEN C.Month   END DESC, 
     CASE WHEN @Ordering = 'desc' THEN A.StageName  END DESC 
+0

Самый дорогой вид в вашем плане происходит из-за итератора 'Stream Aggregate', поскольку этот итератор ожидает отсортированные входы, а итератор' Stream Aggregate' существует из-за вашего предложения 'inner group by'. Попытайтесь получить уникальные строки, не используя предложение 'Group by', которое потеряет« Сводную совокупность », и сортировка также исчезнет. –

+0

Спасибо @ M.Ali - это невозможно, к сожалению, поскольку мне нужно сгруппировать все записи в таблице, чтобы рассчитать общее среднее значение. – Philip

ответ

0

Хотя я понимаю, что вы не можете избавиться от GROUP BY на разных колонках в суб-запроса, вы можете сделать его проще для системы.

В настоящее время у вас есть

S.StageNumber, 
S.StageName, 
MONTH(TimeIn), 
DATENAME(MONTH,TimeIn), 
YEAR(TimeIn) 

который я предполагаю, что это довольно много данных, чтобы пройти. Позвольте мне сделать некоторые предположения:

S.StageNumber, -- int, 4 bytes 
S.StageName, -- string, 20 bytes 
MONTH(TimeIn), -- int, 4 bytes 
DATENAME(MONTH,TimeIn), -- string 5 bytes 
YEAR(TimeIn) -- int, 4 byte 

Теперь, есть некоторая зависимость:

  • Если вы знаете МЕСЯЦ (номер), то теперь также название его
  • Я предполагая, что StageName + StageNumber уникален и напрямую связан с StageID. Если нет, вам может понадобиться снова GROUP BY во внешнем слое.

Это приведет нас к

S.StageID, -- int, 4 bytes 
MONTH(TimeIn), -- int, 4 bytes 
YEAR(TimeIn) -- int, 4 byte 

Это означает, что сортировка для GROUP BY должна работать только через 12 байт на запись, а не 37 байт на запись это было раньше, и номер своего рода довольно бит быстрее, чем строки (например, из-за верхнего/нижнего регистра, акцентов и т. д.)

Я попытался соответствующим образом переписать запрос (Untested!). Я также переместил выборку Month-information в отдельную временную таблицу, это должно помочь оптимизатору запросов немного.

SELECT DISTINCT Month,MonthName,Year 
    INTO #dates 
    FROM Calendar 
WHERE DATE >= @StartDate AND DATE < DATEADD (d,1,@EndDate) 

CREATE UNIQUE CLUSTERED INDEX uq0_#dates ON #dates (Month,Year) 

SELECT 
     A.StageName, 
     C.Month, 
     C.MonthName as Label, 
     C.Year, 
     isnull(A.Average,0) as Data 
    FROM 
    ( 
     SELECT S.StageName, 
       MonthNumber, 
       Year, 
       Average 
      FROM ( 
        SELECT VM.StageID, 
         MONTH(TimeIn) as MonthNumber, 
         YEAR(TimeIn) as Year, 
         ISNULL(AVG(DATEDIFF(mi,TimeIn,isnull(TimeOut,@TodayDate))),0) as Average 
        FROM 
         VisitMovement VM 
        WHERE 
         (VM.TimeIn >= @StartDate AND 
         VM.TimeIn < DATEADD (d,1,@EndDate)) AND 
         (VM.TimeOut < DATEADD (d,1,@EndDate) OR VM.TimeOut IS NULL) 
        GROUP BY 
         VM.StageID, 
         MONTH(TimeIn), 
         YEAR(TimeIn) 
       ) grp 
      JOIN Stage S 
      ON S.StageID = grp.StageID 
     ) 
    ) A 
    RIGHT OUTER JOIN #dates C 
    on 
     A.MonthNumber = C.Month and 
     -- A.Month = C.MonthName and 
     A.Year = C.Year 
    ORDER BY 
     CASE WHEN @Ordering = 'asc' THEN C.Year   END ASC, 
     CASE WHEN @Ordering = 'asc' THEN C.Month   END ASC, 
     CASE WHEN @Ordering = 'asc' THEN A.StageName  END ASC, 
     CASE WHEN @Ordering = 'desc' THEN C.Year   END DESC, 
     CASE WHEN @Ordering = 'desc' THEN C.Month   END DESC, 
     CASE WHEN @Ordering = 'desc' THEN A.StageName  END DESC 

Надеюсь, что это поможет.

+0

Многие извинения за задержанный ответ @deroby.Я был вне Xmas/Новый год. Спасибо за ваше решение и с Новым Годом :) – Philip

0

Поскольку order by должен оценить каждую строку, я не думаю, что это может обеспечить оптимальное использование индексов. Замена order by на row_number() для сортировки по умолчанию и только изменение ордера однажды должно, по крайней мере, предотвратить множественные оценки @Ordering для одной строки.

В псевдокоде ниже исходный запрос помещается в CTE. Row_number определяет восходящий вид и ниже КТР порядок обратный, если это необходимо:

;with cte as 
(
    SELECT 
     A.StageName, 
     C.Month, 
     ..., 
     row_number() over (order by C.Year,C.Month,A.StageName) sortOrder 
    FROM 
     ...rest of the query, excluding the order by 
) 
select * --or list the columns without the sortOrder 
from cte 
order by sortOrder * case @Ordering when 'desc' then -1 else 1 end 
+0

Спасибо за это @ Me.Name - оцените совет. Однако использование подхода cte делает запрос еще более медленным, к сожалению. При использовании подхода CTE он также помещает желтый пояс в верхнюю часть операции Сортировка. – Philip