Помощь в повышении производительности SQL Server 2008 CTE - PullRequest
1 голос
/ 18 февраля 2011

У меня есть SQL Server 2008 CTE, который отвечает за возврат Top Review для местоположения.

CTE обернут в UDF (Table-Valued) и объединен в поле LocationId , так что я могу получить лучший обзор для каждого местоположения.

Количество элементов :

1 Местоположение имеет 0-много PostLocations
1 PostLocation имеет 1 Post
1 Сообщение имеет 1 Отзыв

Вот UDF:

CREATE FUNCTION [dbo].[Review_HighestRated_Aggregated_ByLocation]
(   

)
RETURNS TABLE 
AS
RETURN 
(
    WITH [RankedLocations] AS
    (
        SELECT      PL.LocationId, 
                    R.Rating, 
                    P.PostID, 
                    P.UniqueUri, 
                    P.Content, 
                    ROW_NUMBER() OVER (PARTITION BY PL.LocationId ORDER BY R.Rating DESC, P.LocationTypeId, P.CreatedOn DESC) As ScoreRank

        From        dbo.PostLocations As PL
        INNER JOIN  dbo.Posts As P
        ON          P.PostId = PL.PostId
        INNER JOIN  dbo.Reviews As R
        ON          R.PostId = P.PostId

        WHERE       R.ReviewTypeId <> 5
        AND         P.Content IS NOT NULL
    )

    SELECT  LocationId, Rating, PostID, UniqueUri, Content
    FROM    RankedLocations
    WHERE   ScoreRank = 1
)

Вот пример того, как я его использую:

select l.LocationId, l.Name, l.UniqueUri, r.UniqueUri, r.Content
from @Locations l -- temp table containing around 18 location ids
inner join dbo.Review_HighestRated_Aggregated_ByLocation() r 
on l.LocationId = r.LocationId

Приведенный выше запрос занимает 15 секунд , что недопустимо. Без присоединения к UDF это займет 0 секунд.

Есть идеи, как мне его улучшить?

Если я посмотрю на план выполнения, это SORT , который берет 98% от стоимости выполнения. Стоимость операций ввода-вывода / поддерева для этой операции составляет ~ 300.

Я надеялся, что план выполнения даст мне подсказку для индекса, который я мог бы создать, чтобы повысить стоимость, но я ничего не получаю.

Есть идеи?

Ответы [ 3 ]

2 голосов
/ 22 февраля 2011

Итак, я обнаружил проблему с производительностью, и это был не CTE, а то, как я его использовал.

У меня есть несколько таблиц поиска, в частности одна для Типа местоположения (Улица = 7, Город = 5 и т. Д.).

Таким образом, чтобы мой SQL был свободным и непротиворечивым (и избегал жестко закодированных магических чисел), я создал скалярную функцию-обертку, которая возвращает значение evuivalent на основе строки, например:

DECLARE @Street_LocationType = [dbo].[ToLocationTypeId]('Street')

Функция чрезвычайно проста, всего лишь серия операторов CASE.

Но я использовал свой CTE так:

SELECT      a.LocationId, b.Content, b.UniqueUri
FROM        [dbo].[Locations] a
INNER JOIN  dbo.Review_HighestRated_Aggregated_ByLocation()  b -- UDF with CTE
ON          a.LocationId = b.LocationId        
WHERE       a.LocationTypeId = @Street_LocationType

Так что я даже не использовал его на самом CTE, я использовал его в качестве фильтра в таблице Locations.

Если я изменю вышеприведенное значение для жесткого кодирования значения (например, 7), время выполнения процедуры сократится с 13 секунд до 2 секунд.

Я не понимаю, но это решило проблему. Я заметил, что когда процедура выполнялась плохо, операция «SORT» в плане запросов имела приблизительное число строк = 32 000, что в основном соответствует каждому сообщению в системе.

После моих изменений предполагаемое количество строк равно 1 (как и должно быть).

Действительно странная деятельность.

0 голосов
/ 18 февраля 2011

Если вашей табличной функции не нужны параметры, рассмотрите возможность использования VIEW вместо UDF.Вероятно, это решает проблему производительности.

0 голосов
/ 18 февраля 2011

Чтобы преобразовать CTE и UDF в VIEW:

DROP FUNCTION [dbo].[Review_HighestRated_Aggregated_ByLocation]
GO

CREATE VIEW Review_HighestRated_Aggregated_ByLocation
AS
SELECT  LocationId, Rating, PostID, UniqueUri, Content
FROM
(
    SELECT      PL.LocationId, 
                R.Rating, 
                P.PostID, 
                P.UniqueUri, 
                P.Content, 
                ROW_NUMBER() OVER (PARTITION BY PL.LocationId ORDER BY R.Rating DESC, P.LocationTypeId, P.CreatedOn DESC) As ScoreRank
    From        dbo.PostLocations As PL
    INNER JOIN  dbo.Posts As P
    ON          P.PostId = PL.PostId
    INNER JOIN  dbo.Reviews As R
    ON          R.PostId = P.PostId
    WHERE       R.ReviewTypeId <> 5
    AND         P.Content IS NOT NULL
) RankedLocations
WHERE   ScoreRank = 1

GO

Пример запроса OP пересмотрен с использованием нового VIEW:

select l.LocationId, l.Name, l.UniqueUri, r.UniqueUri, r.Content
from @Locations l -- temp table containing around 18 location ids
inner join Review_HighestRated_Aggregated_ByLocation r 
on l.LocationId = r.LocationId
...