zoukankan      html  css  js  c++  java
  • 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

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

  • 相关阅读:
    Mondriaan's Dream POJ
    H
    Superdoku Kattis
    Corn Fields POJ
    旅行的意义 Gym
    G
    J
    Welcome Party ZOJ
    redis入门到精通系列(三):key的通用操作和redis内部db的通用操作
    redis入门到精通系列(二):redis操作的两个实践案例
  • 原文地址:https://www.cnblogs.com/mc67/p/4825375.html
Copyright © 2011-2022 走看看