SQL性能:WHERE与WHERE(ROW_NUMBER)

时间:2010-07-09 12:49:43

标签: sql sql-server performance

我希望在表格中获得第n个到第m个记录,以下2个解决方案中的最佳选择是什么:

解决方案1:

    SELECT * FROM Table WHERE ID >= n AND ID <= m

解决方案2:

    SELECT * FROM 
                (SELECT *, 
                        ROW_NUMBER() OVER (ORDER BY ID) AS row 
                 FROM Table 
                )a 
    WHERE row >= n AND row <= m

4 个答案:

答案 0 :(得分:53)

正如其他人已经指出的那样,查询会返回不同的结果,并将苹果与橙子进行比较。

但基本问题仍然存在:更快:密钥集驱动的分页或rownumber驱动的分页?

密钥集分页

键集驱动的分页依赖于记住最后显示的页面的顶部和底部键,并根据顶部/最后一个键集请求下一组或上一组行:

下一页:

select top (<pagesize>) ...
from <table>
where key > @last_key_on_current_page
order by key;

上一页:

select top (<pagesize>)
from <table>
where key < @first_key_on_current_page
order by key desc;

这种方法比ROW_NUMBER方法或MySQL的等效LIMIT方法有两个主要优点:

  • 正确:与基于行号的方法不同,它正确处理新条目和删除条目。第4页的最后一行没有显示为第一行,因为第2页的第23行在此期间被删除了。行之间的行也不会神秘地消失。这些异常在基于row_number的方法中很常见,但基于密钥集的解决方案在避免它们方面做得更好。
  • 快速:所有操作都可以通过快速行定位然后沿所需方向进行范围扫描来解决

然而,这种方法实施起来很难,普通程序员难以理解,工具也不支持。

行号驱动

这是Linq查询引入的常用方法:

select ...
from (
  select ..., row_number() over (...) as rn
  from table)
where rn between @firstRow and @lastRow;

(或使用TOP的类似查询) 这种方法非常容易实现,并且得到工具的支持(特别是Linq .Limit和.Take运算符)。但是这种方法保证扫描索引以计算行数。这种方法对于第1页来说通常非常快,并且随着页面数越来越高而逐渐变慢。

作为奖励,使用此解决方案很容易更改排序顺序(只需更改OVER子句)。

总的来说,考虑到基于ROW_NUMBER()的解决方案的简易性,他们对Linq的支持,使用基于ROW_NUMBER的中等数据集任意订单的简单性就足够了。对于大型和超大型数据集,ROW_NUMBER()可能会出现严重的性能问题。

要考虑的另一件事是,通常有一定的访问模式。通常,前几页很热,而10之后的页面基本上从未被查看过(例如,最近的帖子)。在这种情况下,ROW_NUMBER()用于访问底部页面(显示页面,其中必须计算大量行以获取起始结果行)的惩罚可能会被忽略。

最后,键集分页非常适合字典导航,ROW_NUMBER()无法轻松容纳。字典导航是用户可以导航到某些锚点而不是使用页码,而不是使用页码。典型的例子是像侧边栏这样的联系人Rolodex,你点击M然后导航到以M开头的第一个客户名称。

答案 1 :(得分:11)

第二个答案是您的最佳选择。它考虑到您的ID列中可能存在漏洞的事实。我将它重写为CTE而不是子查询...

;WITH MyCTE AS
(SELECT  *,  
         ROW_NUMBER() OVER (ORDER BY ID) AS row  
FROM     Table)
SELECT   *
FROM     MyCTE
WHERE    row >= @start 
         AND row <= @end

答案 2 :(得分:2)

他们是不同的查询。

假设ID是代理键,它可能有间隙。 ROW_NUMBER将是连续的。

如果你可以保证数据没有空白,那么第一个因为我希望它被编入索引。第二个更“正确”。

答案 3 :(得分:-5)

SELECT * FROM Table WHERE ID BETWEEN N AND N

可能? (未经测试,我生锈了)