如何从T-SQL中的sorting表中的行M开始获取N行

有一个简单的方法可以从任何表中获得前N行:

SELECT TOP 10 * FROM MyTable ORDER BY MyColumn 

是否有任何有效的方式来查询从行N开始的M行

例如,

 Id Value 1 a 2 b 3 c 4 d 5 e 6 f 

和这样的查询

 SELECT [3,2] * FROM MyTable ORDER BY MyColumn /* hypothetical syntax */ 

查询从3行开始的2行,即返回3d和4行。

更新如果您使用的是SQL 2012,则添加了新的语法以使其变得非常简单。 请参阅使用此查询实现分页(跳过/带出)function

我想最优雅的是使用ROW_NUMBER函数(可从MS SQL Server 2005中获得):

 WITH NumberedMyTable AS ( SELECT Id, Value, ROW_NUMBER() OVER (ORDER BY Id) AS RowNumber FROM MyTable ) SELECT Id, Value FROM NumberedMyTable WHERE RowNumber BETWEEN @From AND @To 

在这个线程和networking上的其他地方提出的build议的问题是,所有提出的解决scheme都相对于logging数量以线性时间运行。 例如,考虑如下的查询。

 select * from ( select Row_Number() over (order by ClusteredIndexField) as RowNumber, * from MyTable ) as PagedTable where RowNumber between @LowestRowNumber and @HighestRowNumber; 

获取页面1时,查询需要0.577秒。 但是,当获取页面15,619时,这个相同的查询需要2分55秒。

通过创build一个logging号,索引交叉表,我们可以大大改善这一点,如下面的查询所示。 交叉表称为PagedTable,并且是非持久性的。

 select * from ( select Row_Number() over (order by Field1 asc, Field2 asc, Field3 asc) as RowNumber, ClusteredIndexField from MyTable ) as PagedTable left join MyTable on MyTable.ClusteredIndexField = PagedTable.ClusteredIndexField where RowNumber between @LowestRowNumber and @HighestRowNumber; 

和前面的例子一样,我在780,928条logging的非常宽的表上testing了这个。 我使用了50页的页面,导致了15,619页。

页面1(第一页)所花费的总时间是0.413秒。 第15,619页(最后一页)所用的总时间为0.987秒,仅为第1页的两倍。这些时间是使用SQL Server Profiler测量的,而DBMS是SQL Server 2008 R2。

这种解决scheme适用于任何情况下,当您通过索引sorting表。 索引不一定要聚集或简单。 在我的情况下,索引由三个字段组成:varchar(50)asc,varchar(15)asc,numeric(19,0)asc。 尽pipe这个繁琐的索引performance非常出色,但它进一步certificate了这种方法的有效性。

但是,Row_Number窗口函数中的order by子句对应于索引是非常重要的。 否则,性能会降低到与第一个例子相同的水平。

这种方法仍然需要线性操作来生成非持久性交叉表,但由于这只是添加了行号的索引,所以发生得非常快。 在我的情况下,它花了0.347秒,但我的情况有varchars需要复制。 一个数字索引将花费更less的时间。

出于所有实际的目的,这种devise将服务器端分页的缩放从线性操作减小到对数操作,从而允许大表的缩放。 以下是完整的解决scheme。

 -- For a sproc, make these your input parameters declare @PageSize int = 50, @Page int = 15619; -- For a sproc, make these your output parameters declare @RecordCount int = (select count(*) from MyTable); declare @PageCount int = ceiling(convert(float, @RecordCount) / @PageSize); declare @Offset int = (@Page - 1) * @PageSize; declare @LowestRowNumber int = @Offset; declare @HighestRowNumber int = @Offset + @PageSize - 1; select @RecordCount as RecordCount, @PageCount as PageCount, @Offset as Offset, @LowestRowNumber as LowestRowNumber, @HighestRowNumber as HighestRowNumber; select * from ( select Row_Number() over (order by Field1 asc, Field2 asc, Field3 asc) as RowNumber, ClusteredIndexField from MyTable ) as PagedTable left join MyTable on MyTable.ClusteredIndexField = PagedTable.ClusteredIndexField where RowNumber between @LowestRowNumber and @HighestRowNumber; 

如果您想从第25条logging中select100条logging:

 select TOP 100 * from TableName where PrimaryKeyField NOT IN(Select TOP 24 PrimaryKeyField from TableName); 

SQL 2012中,您可以使用OFFSETFETCH

 SELECT * FROM MyTable ORDER BY MyColumn OFFSET @N ROWS FETCH NEXT @M ROWS ONLY; 

我个人比较喜欢:

 DECLARE @CurrentSetNumber int = 0; DECLARE @NumRowsInSet int = 2; SELECT * FROM MyTable ORDER BY MyColumn OFFSET @NumRowsInSet * @CurrentSetNumber ROWS FETCH NEXT @NumRowsInSet ROWS ONLY; SET @CurrentSetNumber = @CurrentSetNumber + 1; 

其中@NumRowsInSet是要返回的行数, @NumRowsInSet @CurrentSetNumber是要跳过的@NumRowsInSet的数量。

丑,哈克,但应该工作:

 select top(M + N - 1) * from TableName except select top(N - 1) * from TableName 

可能对小结果很好,适用于所有版本的TSQL:

 SELECT * FROM (SELECT TOP (N) * FROM (SELECT TOP (M + N - 1) FROM Table ORDER BY MyColumn) qasc ORDER BY MyColumn DESC) qdesc ORDER BY MyColumn 
  -- *some* implementations may support this syntax (mysql?) SELECT Id,Value FROM xxx ORDER BY Id LIMIT 2 , 0 ; -- Separate LIMIT, OFFSET SELECT Id,Value FROM xxx ORDER BY Id LIMIT 2 OFFSET 2 ; -- SQL-2008 syntax SELECT Id,Value FROM xxx ORDER BY Id OFFSET 4 FETCH NEXT 2 ROWS ONLY ; 
 @start = 3 @records = 2 Select ID, Value From (SELECT ROW_NUMBER() OVER(ORDER BY ID) AS RowNum, ID,Value From MyTable) as sub Where sub.RowNum between @start and @start+@records 

这是一种方法。 如果你的谷歌SQL寻呼有很多其他人。

以下是简单查询将从表格的第M + 1行列出N行。 用您的首选号码replaceM和N.

 Select Top N B.PrimaryKeyColumn from (SELECT top M PrimaryKeyColumn FROM MyTable ) A right outer join MyTable B on A.PrimaryKeyColumn = B.PrimaryKeyColumn where A.PrimaryKeyColumn IS NULL 

请让我知道这是否有用你的情况。

这就是你如何在没有主键的表上实现相同的目标:

 select * from ( select row_number() over(order by (select 0)) rowNum,* from your_table ) tmp where tmp.rowNum between 20 and 30 -- any numbers you need 

我在这里阅读了所有的答案,最后提出了一个简单易用的解决scheme。 性能问题来自BETWEEN声明,而不是行号本身的产生。 所以我使用了一个algorithm来通过传递页码和logging数来进行dynamic分页。

传递不是开始行和行数,而是“行数(500)”和“页码(4)”,它们是行1501-2000。这些值可以被存储过程variables替代,所以你不是locking在使用特定的寻呼量。

 select * from ( select (((ROW_NUMBER() OVER(ORDER BY MyField) - 1) / 500) + 1) AS PageNum , * from MyTable ) as PagedTable where PageNum = 4; 

这个线程是相当古老,但目前你可以做到这一点:更清洁imho

 SELECT * FROM Sales.SalesOrderDetail ORDER BY SalesOrderDetailID OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY; GO 

来源: http : //blog.sqlauthority.com/2013/12/30/sql-server-mysql-limit-and-offset-skip-and-return-only-next-few-rows-paging-solution/

为了在SQL Server中执行此操作,您必须按列sorting查询,以便您可以指定所需的行。

这样做时不能使用“TOP”关键字,必须使用偏移N行取下M行。

例:

 select * from table order by [some_column] offset 10 rows FETCH NEXT 10 rows only 

你可以在这里了解更多: https : //technet.microsoft.com/pt-br/library/gg699618%28v=sql.110%29.aspx

find第N行的id然后获取id大于或等于的前M行

将@N声明为int
设置@N = 2
将@M声明为int
设置@M = 3

将@Nid声明为int

设置@Nid = max(id)
从
   (select顶部@N *
来自MyTable
按IDsorting)

select顶部@M *
来自MyTable
其中id> = @Nid
按IDsorting

像这样的东西…但我在这里做了一些假设(例如,你想通过ID命令)

对于T-SQL有一个相当直接的方法,但是我不确定如果跳过大量的行是否预先有效。

 SELECT TOP numberYouWantToTake [yourColumns...] FROM yourTable WHERE yourIDColumn NOT IN ( SELECT TOP numberYouWantToSkip yourIDColumn FROM yourTable ORDER BY yourOrderColumn ) ORDER BY yourOrderColumn 

如果您使用的是.Net,那么您可以将以下内容用于数据结果中的IEnumerable:

 IEnumerable<yourDataType> yourSelectedData = yourDataInAnIEnumerable.Skip(nubmerYouWantToSkip).Take(numberYouWantToTake); 

这背后是你从数据存储获取所有的数据。

为什么不做两个查询:

 select top(M+N-1) * from table into temp tmp_final with no log; select top(N-1) * from tmp_final order by id desc; 
 SELECT * FROM ( SELECT Row_Number() Over (Order by (Select 1)) as RawKey, * FROM [Alzh].[dbo].[DM_THD_TRANS_FY14] ) AS foo WHERE RawKey between 17210400 and 17210500