Row_Number()使用ORDER BY CASE时的CTE性能

时间:2010-07-12 23:51:32

标签: sql-server sql-order-by case common-table-expression row-number

我有一个表,我想做分页和排序,并且能够得到类似于以下的查询来完成工作(真正的查询更多地涉及连接等)。

WITH NumberedPosts (PostID, RowNum) AS
(
    SELECT PostID, ROW_NUMBER() OVER (ORDER BY
        CASE WHEN @sortCol = 'User' THEN User END DESC,
        CASE WHEN @sortCol = 'Date' THEN Date END DESC,
        CASE WHEN @sortCol = 'Email' THEN Email END DESC) as RowNum
   FROM Post
)
INSERT INTO #temp(PostID, User, Date, Email)
SELECT PostID, User, Date, Email
FROM Post
WHERE NumberedPosts.RowNum BETWEEN @start and (@start + @pageSize)
      AND NumberedPosts.PostID = Post.PostID

问题在于,与普通ORDER BY Date desc子句相比,使用CASE语句(至少10倍减速)时性能会严重下降。查看查询计划,看起来所有列仍在排序,即使它们与@sortCol限定符不匹配。

有没有办法让它以接近'原生'的速度执行?动态SQL是这个问题的最佳候选者吗?谢谢!

4 个答案:

答案 0 :(得分:3)

最好使用三个硬编码查询(在基于@sortCol的适当IF语句中)或动态SQL执行此操作。

你可能可以使用UNION ALL三个不同的查询(基于执行所有JOIN的基础CTE),其中只有一个返回@sortCol的行,但我必须在推荐之前对其进行分析它:

WITH BasePosts(PostID, User, Date, Email) AS (
    SELECT PostID, User, Date, Email
    FROM Posts -- This is your complicated query
)
,NumberedPosts (PostID, User, Date, Email, RowNum) AS
(
    SELECT PostID, User, Date, Email, ROW_NUMBER() OVER (ORDER BY User DESC)
    FROM BasePosts
    WHERE @sortCol = 'User'

    UNION ALL

    SELECT PostID, User, Date, Email, ROW_NUMBER() OVER (ORDER BY Date DESC)
    FROM BasePosts
    WHERE @sortCol = 'Date'

    UNION ALL

    SELECT PostID, User, Date, Email, ROW_NUMBER() OVER (ORDER BY Email DESC)
    FROM BasePosts
    WHERE @sortCol = 'Email'
)
INSERT INTO #temp(PostID, User, Date, Email)
SELECT PostID, User, Date, Email
FROM NumberedPosts
WHERE NumberedPosts.RowNum BETWEEN @start and (@start + @pageSize)

答案 1 :(得分:2)

我肯定会继续使用动态SQL路由(使用带参数的sp_executesql来避免任何注入攻击)。使用CASE方法,您可以立即阻止SQL Server使用任何有助于排序过程的相关索引。

答案 2 :(得分:2)

没有任何理由可以两次查询帖子表。您可以使用动态路由并在性能上解决这些问题,也可以创建由@sortCol参数确定的3个查询。冗余代码除了row_num和按部分排序,但有时如果速度很关键,你会放弃可维护性。

If @sortCol = 'User' 
Begin
  Select... Order by User
End

If @sortCol = 'Date' 
Begin
  Select .... Order by Date 
end

If @sortCol = 'Email' 
Begin
  Select... Order by Email
End 

答案 3 :(得分:0)

这应该有效,但不确定它是否会提高性能:

WITH NumberedPosts (PostID, RowNum) AS
(
    SELECT PostID, ROW_NUMBER() OVER (ORDER BY
        CASE WHEN @sortCol = 'User' THEN User 
             WHEN @sortCol = 'Date' THEN Date
             WHEN @sortCol = 'Email' THEN Email
        END DESC) as RowNum
   FROM Post
)
INSERT INTO #temp(PostID, User, Date, Email)
SELECT PostID, User, Date, Email
FROM Post
WHERE NumberedPosts.RowNum BETWEEN @start and (@start + @pageSize)
      AND NumberedPosts.PostID = Post.PostID