我有一个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。
我希望执行计划会给我一个提示,我可以创建一个索引以提高成本,但我什么都没得到。
有什么想法吗?
答案 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。可能它解决了性能问题。