经常写SQL查询脚本的朋友很可能会遇到分页查询的问题。在MSSQL2000及以前的版本中,分页的写法大多采用纯顶层嵌套的方式,比较复杂,效率较低。从MSSQL2005开始,SQLServer提供了一个内置函数ROW_NUMBER,这是一个非常神奇的功能。从MSSQL2012开始,SQLServer提供了分页的offset方式。使用offsetstartPagerowsfetchnextpageSizerowsonly方法进行分页。今天我们就来说说ROW_NUMBER和offset的语法以及它们在分页中的应用。ROW_NUMBER的含义和语法定义ROW_NUMBER实现了对结果集输出的编号。具体来说,返回结果集分区内行的顺序数,每个分区中的第一行从1开始。ROW_NUMBER()OVER([PARTITIONBYfield,]order_by_clause)PARTITIONBY:将FROM子句生成的结果集划分为应用ROW_NUMBER函数的分区。value_expression指定对结果集进行分区的列。如果未指定PARTITIONBY,该函数会将查询结果集中的所有行视为一个组。order_by_clause:子句确定在特定分区中为行分配***ROW_NUMBER的顺序。orderby子句是必需的。返回值:bigint。结果集分区中行的序号。offset的含义和语法定义offset是orderby的子句,主要用来限制返回的行数,也适用于分页。它仅从MSSQL2012开始受支持。语法结构如下:FETCH{NEXT}{integer_constant|fetch_row_count_expression}{ROWS}ONLYfetch_row_count_expression可以是变量、参数或常量标量子查询。使用子查询时,它不能引用外部查询范围中定义的任何列。也就是说,它不能与外部查询相关联。结合分页,语法syntax:offsetstartPagerowsfetchnextpageSizerowsonlywherestartpage:startPage=(@page-1)*@rows,pagesize:pageSize=@rowsdemo数据准备为了方便讲解,我们准备了一些demo数据,这是一个简单的业务sales表,字段只有salesperson、salesarea和sales,如下:declare@saletable(FNamenvarchar(50),FDistrictnvarchar(50),FAmountdecimal(28,10));insertinto@salevalues('张三','北京',20000),('张三','上海',50000),('张三','深圳',40000),('张三','广州',30000),('李四','北京',30000),('李斯','上海',50000),('李斯','深圳',40000),('李斯','广州',10000),('王Er','北京',70000),('旺二','上海',10000),('旺二','深圳',60000),('旺二','广州',20000),('马六','北京',80000),('马六','上海',20000),('马六','深圳',70000),('马六','广州',60000)准备好演示数据使用ROW_NUMBER分页。比如我们想按销售人员+销售区域排序后,在一个页面上每4条记录显示一次。写法如下:declare@pagesizeint=4;--每页记录数declare@pagenumint=1;页面选择v.*from(selectrow_number()over(orderbyFName,FDistrict)asFRowIndex,*from@sale)asvwherev.FRowIndexbetween@pagesize*(@pagenum-1)+1and@pagenum*@pagesize;分页查询ROW_NUMBER函数属于SQL中的热名(即新确定的名字FRowIndex)只能出现在select子句中,需要放在子查询中。也可以先定义子查询,然后直接引用。语法如下:declare@pagesizeint=4;--每页记录数declare@pagenumint=1;--页数withsaledataas(selectrow_number()over(orderbyFName,FDistrict)asFRowIndex,*from@sale)select*fromsaledatawhereFRowIndexbetween@pagesize*(@pagenum-1)+1and@pagenum*@pagesize;分页查询ROW_NUMBER做分页查询,经过反复应用测试,效率还是很高的。完整的测试脚本见下图:使用OFFSET实现分页重复的部分代码不再赘述。查询时需要注意offset是OrderBy的一个子句,不能独立存在。语法结构如下:select*from@saleorderbyFName,FDistrictoffset(@pagenum-1)*@pagesizerowsfetchnext@pagesizerowsononly返回与使用row_number相同的结果。完整测试脚本见下图:希望对您有所帮助!
