SQLSERVER 中的几种分页方法

SQLSERVER中的几种分页方法

      这些分页方法虽然不是最优的方案,但你可以当做sql语句练习,熟练掌握sql语句和分页原理, 

当然,文章的最后,我也会给出一种最好的分页方法。

--测试数据库
CREATE TABLE STU
(
   ID INT NOT NULL PRIMARY KEY IDENTITY(1,1),
   NAME VARCHAR(40)
)
--插入100一条数据
DECLARE @I INT
SET @I=1
WHILE(@I<=100)
BEGIN
     INSERT STU VALUES('JACK'+CAST( @I AS VARCHAR(3)))
     SET @I=@I+1;
END

需求:(每页显示10条,取出第3页)  PageSize=10 PageIndex=3

方法一:NOT IN 方式 

  SELECT TOP 10 * FROM STU
       WHERE ID NOT IN(
         SELECT TOP 20 ID FROM STU
         ORDER BY ID ASC
       ) ORDER BY ID
        
 --通用表达式
 /*
      SELECT TOP PageSize * FROM TABLENAME
      WHERE  ID NOT IN(
      SELECT TOP (PageIndex-1)*PageSize ID FROM STU
      ORDER BY ID ASC       
     )ORDER BY ID ASC
 */

方法二:row_number 分页

 SELECT * FROM (
     SELECT ROW_NUMBER() OVER(ORDER BY ID) AS ROWID,* FROM  STU
     ) AS T 
     WHERE T.ROWID BETWEEN 21 AND 30 
     ORDER BY ID
     
  --通用表达式
 /*
     SELECT * FROM (
        SELECT ROW_NUMBER() OVER(ORDER BY ID) AS ROWID,* FROM TABLE_NAME
     )AS T
     WHERE T.ROWID BETWEEN (PageSize-1)*PageIndex+1 AND PageSize*PageIndex
     ORDER BY ID
 
 */

 方法二的语句优化(加上了CTE)

WITH CET
AS
(
  SELECT * ,ROW_NUMBER() OVER(ORDER BY ID) AS ROW_ID
  FROM STU
)
SELECT ID,NAME FROM CET 
WHERE ROW_ID BETWEEN 10 AND 20 

 

方法三:利用ID 大于多少

SELECT TOP 10 * FROM STU WHERE ID>(
     SELECT MAX(ID) FROM (
       SELECT TOP 20 ID FROM STU 
       ORDER BY ID ASC
     )AS T 
 )
 --通用表示式:
 /*
      SELECT TOP PageSize * FROM STU WHERE ID>(
         SELECT MAX(ID) FROM(
            SELECT TOP (PageIndex-1)*PageSize
            ORDER BY ID ASC
         ) AS T
      )
 
 */
   

方法四:asc + desc + top 简称颠倒法把;

SELECT TOP 10 * FROM (
         SELECT TOP 30 * FROM STU ORDER BY ID ASC
)AS T ORDER BY T.ID DESC

-- 通用表达式:
/*
    SELECT TOP PageSize PageSize * FROM (
           SELECT TOP PageSize*PageIndex FROM STU 
           ORDER BY ID ASC
    ) AS T
    ORDER BY T.ID DESC
*/

方法五: IN (可以说值对方法四的一种改进)

    SELECT  TOP 10 * FROM STU WHERE ID IN(
       SELECT TOP 10 ID FROM (
         SELECT TOP 30 ID FROM STU 
         ORDER BY ID ASC
       ) AS T
       ORDER BY T.ID DESC
    )
    /*
      通用表达式:
      
      SELECT TOP PageSize * FROM TABLE WHERE ID IN(
           SELECT TOP PageSize ID FROM (
              SELECT TOP PageSize*PageIndex FROM TABLE
              ORDER BY ID ASC
        )AS T
        ORDER BY T.ID DESC
      )
                 
    */

分页的方法还有很多,如使用游标,和变量表来分页的,
那些效率不是太好,这里就不总结了.

在实际的开发中,不仅仅是分页这个单一的需求,往往掺杂着根据相应的where条件分页
还有可排序的字段 返回记录总条数等等问题,
接下来,我们就做一个完整的存储过程,分页的存储过程,网上有很多的源码,使用的方法也有所不同,有说 row_number 效率高的,有说 top 方法高的,

不管哪种,还有的说,看情况,(具体点看这里:http://www.jb51.net/article/35213.htm)比如:  由此可见在查询页数靠前时,TOP 好一点,页码靠后时row_number好一点,应付百万级的数据都是没问题的,这里,总结一种经过测试的代码(会继续关注这个问题,有更好的代码,我会及时贴出.(总结)....)

(这里,两种方法我都写一下,你也试试吧~)

--第一种 row_number(注意SQL2000不支持row_number()函数,单我相信,应该没有人用sql2000 了 吧)

-- LIUQI 2015 9 28
--测试表 还是 stu
CREATE PROC GETDATA(
     @recordTotal INT OUTPUT,      --输出记录总数
     @tableName   VARCHAR(350),    -- 表名
     @fieldName VARCHAR(300)='*',  --查询字段
     @keyName VARCHAR(200)='ID',   --索引字段
     @pageSize INT  =10,           --每页记录调试
     @pageIndex INT =1,            --当前页
     @orderString varchar(300)='ID',    --排序条件
     @whereString varchar(400)='1=1'     --where条件
     
)AS
BEGIN
     DECLARE @beginRow INT,
             @endRow    INT,
             @tempLimit VARCHAR(200),
             @tempCount NVARCHAR(800),
             @SQL  VARCHAR(800),
             @timeDiff  DATETIME
             
             SET NOCOUNT ON
             SELECT @timeDiff=GETDATE();
             SET @beginRow=(@pageIndex-1)*@pageSize +1
             SET @endRow=@pageIndex*@pageSize;
             SET @tempLimit=' ROWS BETWEEN '+ CAST(@beginRow AS VARCHAR)+' AND '+CAST(@endRow AS VARCHAR);
             
             
             --输出总记录条数;
             --有中观点认为:当集合总数不会有变化时,没有必要每次都输出总记录条数
             --第一次输出之后,应该把它保存起来,以备后是只用
             
             SET @tempCount='SELECT @recordTotal=COUNT(*) FROM ( SELECT '+@keyName+' FROM '+@tableName+' WHERE '+@whereString +') AS MY_TEMP'    
             EXEC SP_EXECUTESQL @tempCount,N'@recordTotal INT OUTPUT ',@recordTotal OUTPUT;
             
             --返回主要的结果结合;
             
             SET @SQL='SELECT * FROM (SELECT ROW_NUMBER() OVER(ORDER BY '+@orderString+') AS ROWS,'+@fieldName+' FROM '+@tableName+' WHERE '+@whereString+') AS MAIN_TEMP WHERE'+@tempLimit                            
                                                
             PRINT @SQL
             EXEC(@SQL)
             SELECT DATEDIFF(MS,@timeDiff,GETDATE()) AS MSTIME
             SET NOCOUNT OFF     

END
GO

--测试;
       DECLARE @TOTAL INT
       EXEC GETDATA @TOTAL OUTPUT,'STU',' ID NAME ',' ID ',10 ,3
       SELECT @TOTAL

        --测试;
       DECLARE @TOTAL INT
       EXEC GETDATA @TOTAL OUTPUT,'STU',' ID NAME ',' ID ',10 ,3
       SELECT @TOTAL
       
 /*
21    21
22    22
23    23
24    24
25    25
26    26
27    27
28    28
29    29
30    30

16 0 (MSTIME) 经过多次测试(本人的是普通pc机器),第一次都在10-16 左右,第二次查询同样的数据时,都是0

100 (TOTALCOUNT)
 */
View Code

 

第二种:TOP

 ALTER PROC GETDATA2
 ( @pageCount      INT OUTPUT,        --页数
   @recordCount    INT OUTPUT,        --总数
   @tableName      VARCHAR(200)='STU',--表名
   @KEY            VARCHAR(100)='ID', --索引字段
   @Fields         VARCHAR(300)='*',  --字段
   @whereStr       VARCHAR(400)='1=1', --where 条件
   @orderStr       VARCHAR(200)='ID',  --排序字段 目前支持 单个字段
   @Begin          INT=0,              --开始位置  
   @pageIndex      INT =1,       
   @pageSize       INT =10
 )
 AS
 BEGIN
       SET NOCOUNT ON
       SET ANSI_WARNINGS ON
       IF (@pageSize<0 OR @pageIndex<0)
       BEGIN 
       RETURN
       END
       
       DECLARE @NEW_WHERE1 VARCHAR(400),
               @NEW_ORDER1 VARCHAR(200),
               @NEW_ORDER2 VARCHAR(200),
               @SQL VARCHAR(800),
               @SQLCOUNT NVARCHAR(800),
               
               @TOP INT
               
               IF(@Begin<=0)
               SET @Begin=0
               ELSE
               SET @Begin=@Begin-1;
               
               --这个部分 可以使用 WHERE 1=1 来进行替换
               IF ISNULL(@whereStr,'')=''
               SET @NEW_WHERE1=''
               ELSE
               SET @NEW_WHERE1=' WHERE ' +@whereStr
               
               IF ISNULL(@orderStr,'')<>''
               BEGIN
                  SET @NEW_ORDER1='ORDER BY '+@orderStr  +' ASC'  
                  SET @NEW_ORDER2=' ORDER BY T.'+@orderStr+' DESC '
               END
               ELSE
               BEGIN
                    SET @NEW_ORDER1=' ORDER BY ID ASC '
                    SET @NEW_ORDER2=' ORDER BY T.ID DESC '
                    
               END
               
               --接下来 获取总记录条数;
               SET @SQLCOUNT=N'SELECT @recordCount=COUNT(1),@pageCount=CEILING((COUNT(1)+0.0)/'+CAST(@pageSize AS VARCHAR )+') FROM '+@tableName+@NEW_WHERE1
               EXEC SP_EXECUTESQL @SQLCOUNT,N'@recordCount INT OUTPUT,@pageCount INT OUTPUT',@recordCount OUTPUT,@pageCount OUTPUT
               
               --如果输入的当期页码大于实际总数;
               --则把实际总页数赋值给当前页数
               IF @pageIndex>CEILING((@recordCount+0.0)/@pageSize)
               BEGIN
                    SET @pageIndex=CEILING((@recordCount+0.0)/@pageSize)
               END
               
               --拼接sql语句;
                SET @SQL='SELECT '+@Fields+' FROM '+@tableName+'  WHERE '+@KEY + ' IN ('
                      +'SELECT TOP '+LTRIM(STR(@pageSize))+' '+@KEY +' FROM '
                      +'( ' 
                      +'SELECT TOP '+LTRIM(STR(@pageSize*@pageIndex+@Begin))+' '+@KEY+' FROM '+@tableName+@NEW_WHERE1+@NEW_ORDER1
                      +') AS T'+@NEW_ORDER2
                      +') '
                PRINT @SQL
       SET NOCOUNT OFF
 END
 
 --测试:
/*
DECLARE @pageCount INT, 
        @recordCount INT
        
EXEC GETDATA2 @pageCount OUTPUT,@recordCount OUTPUT,'STU'
go
*/
 
View Code

 第二种,还有点地方,不太合理,需要整理下;.

 

posted @ 2015-09-21 10:22  咕-咚  阅读(1140)  评论(0编辑  收藏  举报