我有一个存储过程需要花费大量时间来执行。任何人都可以提出更好的方法,以便获得相同的结果集。
ALTER PROCEDURE [dbo].[spFavoriteRecipesGET]
@USERID INT, @PAGENUMBER INT, @PAGESIZE INT, @SORTDIRECTION VARCHAR(4), @SORTORDER VARCHAR(4),@FILTERBY INT
AS
BEGIN
DECLARE
@ROW_START INT
DECLARE
@ROW_END INT
SET
@ROW_START = (@PageNumber-1)* @PageSize+1
SET
@ROW_END = @PageNumber*@PageSize
DECLARE
@RecipeCount INT
DECLARE
@RESULT_SET_TABLE
TABLE
(
Id INT NOT NULL IDENTITY(1,1),
FavoriteRecipeId INT,
RecipeId INT,
DateAdded DATETIME,
Title NVARCHAR(255),
UrlFriendlyTitle NVARCHAR(250),
[Description] NVARCHAR(MAX),
AverageRatingId FLOAT,
SubmittedById INT,
SubmittedBy VARCHAR(250),
RecipeStateId INT,
RecipeRatingId INT,
ReviewCount INT,
TweaksCount INT,
PhotoCount INT,
ImageName NVARCHAR(50)
)
INSERT INTO @RESULT_SET_TABLE
SELECT
FavoriteRecipes.FavoriteRecipeId,
Recipes.RecipeId,
FavoriteRecipes.DateAdded,
Recipes.Title,
Recipes.UrlFriendlyTitle,
Recipes.[Description],
Recipes.AverageRatingId,
Recipes.SubmittedById,
COALESCE(users.DisplayName,users.UserName,Recipes.SubmittedBy) As SubmittedBy,
Recipes.RecipeStateId,
RecipeReviews.RecipeRatingId,
COUNT(RecipeReviews.Review),
COUNT(RecipeTweaks.Tweak),
COUNT(Photos.PhotoId),
dbo.udfGetRecipePhoto(Recipes.RecipeId) AS ImageName
FROM
FavoriteRecipes
INNER JOIN Recipes ON FavoriteRecipes.RecipeId=Recipes.RecipeId AND Recipes.RecipeStateId <> 3
LEFT OUTER JOIN RecipeReviews ON RecipeReviews.RecipeId=Recipes.RecipeId AND RecipeReviews.ReviewedById=@UserId
AND RecipeReviews.RecipeRatingId= (
SELECT MAX(RecipeReviews.RecipeRatingId)
FROM RecipeReviews
WHERE RecipeReviews.ReviewedById=@UserId
AND RecipeReviews.RecipeId=FavoriteRecipes.RecipeId
)
OR RecipeReviews.RecipeRatingId IS NULL
LEFT OUTER JOIN RecipeTweaks ON RecipeTweaks.RecipeId = Recipes.RecipeId AND RecipeTweaks.TweakedById= @UserId
LEFT OUTER JOIN Photos ON Photos.RecipeId = Recipes.RecipeId
AND Photos.UploadedById = @UserId AND Photos.RecipeId = FavoriteRecipes.RecipeId
AND Photos.PhotoTypeId = 1
LEFT OUTER JOIN users ON Recipes.SubmittedById = users.UserId
WHERE
FavoriteRecipes.UserId=@UserId
GROUP BY
FavoriteRecipes.FavoriteRecipeId,
Recipes.RecipeId,
FavoriteRecipes.DateAdded,
Recipes.Title,
Recipes.UrlFriendlyTitle,
Recipes.[Description],
Recipes.AverageRatingId,
Recipes.SubmittedById,
Recipes.SubmittedBy,
Recipes.RecipeStateId,
RecipeReviews.RecipeRatingId,
users.DisplayName,
users.UserName,
Recipes.SubmittedBy;
WITH SortResults
AS
(
SELECT
ROW_NUMBER() OVER (
ORDER BY CASE WHEN @SORTDIRECTION = 't' AND @SORTORDER='a' THEN TITLE END ASC,
CASE WHEN @SORTDIRECTION = 't' AND @SORTORDER='d' THEN TITLE END DESC,
CASE WHEN @SORTDIRECTION = 'r' AND @SORTORDER='a' THEN AverageRatingId END ASC,
CASE WHEN @SORTDIRECTION = 'r' AND @SORTORDER='d' THEN AverageRatingId END DESC,
CASE WHEN @SORTDIRECTION = 'mr' AND @SORTORDER='a' THEN RecipeRatingId END ASC,
CASE WHEN @SORTDIRECTION = 'mr' AND @SORTORDER='d' THEN RecipeRatingId END DESC,
CASE WHEN @SORTDIRECTION = 'd' AND @SORTORDER='a' THEN DateAdded END ASC,
CASE WHEN @SORTDIRECTION = 'd' AND @SORTORDER='d' THEN DateAdded END DESC
) RowNumber,
FavoriteRecipeId,
RecipeId,
DateAdded,
Title,
UrlFriendlyTitle,
[Description],
AverageRatingId,
SubmittedById,
SubmittedBy,
RecipeStateId,
RecipeRatingId,
ReviewCount,
TweaksCount,
PhotoCount,
ImageName
FROM
@RESULT_SET_TABLE
WHERE
((@FILTERBY = 1 AND SubmittedById= @USERID)
OR ( @FILTERBY = 2 AND (SubmittedById <> @USERID OR SubmittedById IS NULL))
OR ( @FILTERBY <> 1 AND @FILTERBY <> 2))
)
SELECT
RowNumber,
FavoriteRecipeId,
RecipeId,
DateAdded,
Title,
UrlFriendlyTitle,
[Description],
AverageRatingId,
SubmittedById,
SubmittedBy,
RecipeStateId,
RecipeRatingId,
ReviewCount,
TweaksCount,
PhotoCount,
ImageName
FROM
SortResults
WHERE
RowNumber BETWEEN @ROW_START AND @ROW_END
print @ROW_START
print @ROW_END
SELECT
@RecipeCount=dbo.udfGetFavRecipesCount(@UserId)
SELECT
@RecipeCount AS RecipeCount
SELECT COUNT(Id) as FilterCount FROM @RESULT_SET_TABLE
WHERE
((@FILTERBY = 1 AND SubmittedById= @USERID)
OR (@FILTERBY = 2 AND (SubmittedById <> @USERID OR SubmittedById IS NULL))
OR (@FILTERBY <> 1 AND @FILTERBY <> 2))
END
答案 0 :(得分:1)
您需要查看执行计划以了解时间的进展。它可能是由UDF引起的索引,表扫描,任何数量的事情。当您对计划进行分析时,请尝试将查询分解为更小的部分,以确定您是否可以对其进行改动。
然后了解ROW_NUMBER以了解您是否可以不使用本地表。
答案 1 :(得分:1)
情侣笔记
索引 - 通常当人们创建使用临时表或表变量的过程时,他们无法意识到您可以在这些对象上创建索引,这可能会产生巨大的性能影响。
UDF - 有时查询处理器将有效内联UDF逻辑,有时不会,仔细查看您的查询计划,看看如何处理它。通常,如果您在相关子查询中手动内联此逻辑,则可以大大提高性能。
答案 2 :(得分:1)
正如其他人所说,了解解释计划的唯一方法。看一下代码,这部分看起来很可疑:
AND RecipeReviews.RecipeRatingId= (
SELECT MAX(RecipeReviews.RecipeRatingId)
FROM RecipeReviews
WHERE RecipeReviews.ReviewedById=@UserId
AND RecipeReviews.RecipeId=FavoriteRecipes.RecipeId
)
一般来说,在连接条件下做一些非平凡的事情是一个坏主意。我会把它分解为一个子选择,因为它是一个外连接,你可能不得不以某种方式将它与RecipeReviews结合起来。
但是:所有这些都是猜测!说明!测量!
答案 3 :(得分:1)
除了UDF可能性能不佳之外,这行代码还关注我
LEFT OUTER JOIN RecipeReviews
ON RecipeReviews.RecipeId=Recipes.RecipeId
AND RecipeReviews.ReviewedById=@UserId
AND RecipeReviews.RecipeRatingId=
(SELECT MAX(RecipeReviews.RecipeRatingId)
FROM RecipeReviews
WHERE RecipeReviews.ReviewedById=@UserId
AND RecipeReviews.RecipeId=FavoriteRecipes.RecipeId )
OR RecipeReviews.RecipeRatingId IS NULL
将子查询用作连接的一部分通常是一种不好的做法。我强烈怀疑这不是你使用的任何索引。并且OR部分没有任何意义,因为左边的连接让你知道了。
重写它以制作派生表。
如果你有很多记录,临时表通常比表变量表现更好,并且可以(也可能应该)被索引。
答案 4 :(得分:0)
您需要在OR
条件周围添加括号。
LEFT OUTER JOIN RecipeReviews
ON RecipeReviews.RecipeId = Recipes.RecipeId
AND RecipeReviews.ReviewedById = @UserId
AND
-- insert open parenthesis here:
(
RecipeReviews.RecipeRatingId = (... subquery ...)
OR RecipeReviews.RecipeRatingId IS NULL
-- insert close parenthesis here:
)
答案 5 :(得分:0)
我要做的第一件非常简单的事就是将所有声明语句移到顶部。
DECLARE @ROW_START INT,
@ROW_END INT,
@RecipeCount INT
DECLARE
@RESULT_SET_TABLE
TABLE
(
Id INT NOT NULL IDENTITY(1,1),
)
下一部分仍然很简单,就像这样:
AND Recipes.RecipeStateId <> 3
AND RecipeTweaks.TweakedById= @UserId
这可以从连接中取出并移动到where子句。如果可以,请更改&lt;&gt;在一个声明中,以便它可以使用索引。
AND RecipeReviews.RecipeRatingId=
(
SELECT MAX(RecipeReviews.RecipeRatingId)
FROM RecipeReviews
WHERE RecipeReviews.ReviewedById=@UserId
AND RecipeReviews.RecipeId=FavoriteRecipes.RecipeId
)
这看起来很疯狂,需要完全重做。