我希望在表格中获得第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
答案 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方法有两个主要优点:
然而,这种方法实施起来很难,普通程序员难以理解,工具也不支持。
这是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
可能? (未经测试,我生锈了)