帮助提高SQL Server 2008 CTE的性能

时间:2011-02-18 00:34:39

标签: performance tsql sql-server-2008 common-table-expression sql-execution-plan

我有一个SQL Server 2008 CTE,它负责返回某个位置的热门评论

CTE包装在UDF(表格值)中,并加入 LocationId 字段,因此我可以获得每个位置的最高评价。

基数

1 位置 0-many PostLocations
1 PostLocation 1 发布
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%。此操作的IO /子树成本约为300。

我希望执行计划会给我一个提示,我可以创建一个索引以提高成本,但我什么都没得到。

有什么想法吗?

3 个答案:

答案 0 :(得分:2)

所以我发现了性能问题,而不是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(应该是这样)。

确实是奇怪的活动。

答案 1 :(得分:0)

将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

答案 2 :(得分:0)

如果表值函数不需要参数,请考虑使用VIEW代替UDF。可能它解决了性能问题。