【问题标题】:Help with Improving Performance of SQL Server 2008 CTE帮助提高 SQL Server 2008 CTE 的性能
【发布时间】:2011-02-18 00:34:39
【问题描述】:

我有一个 SQL Server 2008 CTE,它负责返回一个位置的 Top Review

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

基数

1 位置有 0-many 个 PostLocations
1 个帖子位置有 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。

我希望执行计划能给我一个提示,让我知道我可以创建一个索引来降低成本,但我什么也没得到。

有什么想法吗?

【问题讨论】:

  • R.ReviewTypeId 上有索引吗?您是否有用于外键关系的列的索引(三个表中的PostId)?另外:另一种选择是尝试直接在查询中使用 CTE,而不是在 UDF 中“隐藏”它(这可能非常慢......) - 这有什么不同吗??
  • @marc_s。是的,ReviewTypeId 上有一个索引。是的,PostId 是 Posts 上的集群 PK、PostLocations 上的集群 PK 和评论上的集群 PK 的一部分。我也试过直接使用CTE,没有变化。我尝试创建一个视图,这很好 - 但您不能索引其中包含聚合操作或子查询的视图。目前,我倾向于使用 sql 计划作业每隔一小时左右更新一次的“缓存表”。想法?

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


【解决方案1】:

所以我发现了性能问题,不是 CTE,而是我使用它的方式。

我有几个查找表,其中一个专门用于位置类型(街道 = 7、城市 = 5 等)。

所以为了保持我的 SQL 流畅和一致(并避免硬编码的幻数),我创建了一个包装标量函数,它根据字符串返回有效值,例如:

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(应该是)。

确实是奇怪的活动。

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 使其成为视图没有区别。并且您无法索引具有 CTE 的视图。
    • 我尝试将 CTE 更改为子选择,但您也无法使用子选择索引视图。关于如何将 UDF 编写为可索引视图的任何想法?
    【解决方案3】:

    要将 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
    

    【讨论】:

    • 如果您需要重用 UDF 中的代码,将 UDF 转换为视图会有所帮助。
    • 不,没有区别。如果我能弄清楚如何摆脱 CTE 从而使其成为可索引视图,我认为 pcrofe 的想法会奏效。
    猜你喜欢
    • 1970-01-01
    • 2015-08-01
    • 2020-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多