2

我有一个表我想做分页和排序,并且能够得到类似于下面的查询来完成这项工作(真正的查询涉及更多与连接等)。Row_Number()CTE使用ORDER BY CASE时的性能

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 

麻烦的是使用CASE语句(至少10倍的减速)时,与正常ORDER BY Date desc子句时性能大大降低。查看查询计划,即使它们不匹配@sortCol限定符,也会显示所有列仍在排序。

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

+0

你有一个三级排序(3 DESC和2个逗号)。为什么不仅仅使用单个案例来处理多个案件? – 2010-07-13 02:52:58

回答

2

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

3

更好的做法是使用三种硬编码查询(在基于@sortCol的适当IF语句中)或动态SQL。

你可以用三个不同的查询(基于一个基本的CTE来完成你所有的JOIN)来做一个诡计,其中只有一个返回@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) 
+0

我在查看原始查询之后更新了我的答案 - 一个没有其他结果的情况下会产生一个NULL,这些都会排在一起,但这就是为什么它仍然会对所有内容进行排序。 – 2010-07-13 03:24:19

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 
+0

我甚至没有看他的代码的底部,看他是否会回到帖子,你是绝对正确的,这是不必要的,我已经清理了我的答案。 – 2010-07-13 01:08:20

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 
+1

这个例子实际上有问题,我以为原来的查询有:不同的列(可能)是不同的数据类型,用户和日期和电子邮件都将被转换为相同的类型。 了解了这一点,根据转换为varchar,您的ORDER BY CONVERT(varchar(?),Date)可能甚至无法正常工作(除非它已经是YYYY-MM-DD形式的varchar,在这种情况下,很可能排序好的)。 – 2010-07-13 03:22:57