Answers:
在您发布的查询中:
select * from <table_name>;
没有第100-200行这样的事情,因为您没有指定ORDER BY。除非您出于很多有趣的原因将ORDER BY包括在内,否则无法保证订购,但这并不是重点。
为了说明您的观点,我们使用一个表-我将使用Stack Overflow数据转储中的Users表,并运行以下查询:
SELECT * FROM dbo.Users ORDER BY DisplayName;
默认情况下,DisplayName字段上没有索引,因此SQL Server必须扫描整个表,然后按DisplayName对其进行排序。这是执行计划:
它不是很漂亮-需要大量工作,估计子树成本约为3万。(您可以通过将鼠标悬停在PasteThePlan上的select运算符上来查看它。)那么,如果我们只希望100-200行会发生什么?我们可以在SQL Server 2012+中使用以下语法:
SELECT * FROM dbo.Users ORDER BY DisplayName OFFSET 100 ROWS FETCH NEXT 100 ROWS ONLY;
那的执行计划也很丑陋:
SQL Server仍在扫描整个表以构建排序列表,仅使您的行数为100-200,而成本仍然约为30k。更糟糕的是,每次查询运行时都会重新构建整个列表(因为毕竟可能有人更改了其DisplayName。)
为了使其运行更快,我们可以在DisplayName上创建一个非聚集索引,该索引是表的副本,并按该特定字段排序:
CREATE INDEX IX_DisplayName ON dbo.Users(DisplayName);
使用该索引,我们的查询的执行计划现在执行索引查找:
该查询将立即完成,并且估计的子树成本仅为0.66(而不是30k)。
总之,如果您以支持您经常运行的查询的方式组织数据,那么可以,SQL Server可以使用快捷方式来使查询运行得更快。另一方面,如果您只有堆或聚集索引,那么您就搞砸了。
正如使用非覆盖索引来避免排序时对Brent答案的补充一样,可以通过运行以下命令看到更高的页码潜在问题
SELECT *
FROM dbo.Users
ORDER BY DisplayName
OFFSET 100000 ROWS
FETCH NEXT 100 ROWS ONLY;
执行计划显示,即使TOP运算符随后过滤了除100行之外的所有行,该查询已执行了100,100次。
可以使用以下模式来缓解
WITH T
AS (SELECT Id,
DisplayName
FROM dbo.Users
ORDER BY DisplayName
OFFSET 100000 ROWS
FETCH NEXT 100 ROWS ONLY
)
SELECT U.*
FROM dbo.Users U
JOIN T
ON U.Id = T.Id
ORDER BY T.DisplayName
这会在执行查找之前过滤掉除最后100行之外的所有行,这可能会对大偏移量的速度产生重大影响。
这实际上取决于您如何在查询中实现分页,数据的性质以及系统的配置方式。可以肯定地说,SQL Server将尝试以最少的努力返回您的数据。如果您没有明确的排序顺序,过滤,分组或任何窗口设置,则SQL Server可能会优化查询计划,使其可以仅从磁盘中返回包含查询所需数据的页面,甚至更好的是直接从磁盘中返回页面。缓冲池。一旦开始更改查询以包括排序,分组,开窗和过滤,查询就会变得复杂。
有关于SQL性能非常不错的文章在这里是进入的分页的各种方法,以及它们如何影响查询计划的一些细节。我强烈建议您阅读它,然后尝试他们指出的各种方法中的一些,并查看在您自己的系统上选择了哪种查询计划。