分页存储过程实例剖析心得

数据库
最近修改了个分页存储过程,作为菜鸟,还是从中获益良多,这里就开始今天的分页之旅了。

最近修改了个分页存储过程,作为菜鸟,还是从中获益良多,这里就开始今天的分页之旅了。

1.开始还原

下面先看一下原来的分页存储过程。

  1. ALTER PROCEDURE [dbo].[sp_Sql_Paging] 
  2.     @SqlDataTable        NVARCHAR(4000),        -- 表名 
  3.     @PrimaryKey            NVARCHAR(4000),        -- 主键名称 
  4.     @Fields                NVARCHAR(4000),        -- 要返回的字段 
  5.     @pageSize            INT,                -- 页尺寸 
  6.     @pageIndex            INT,                -- 页码 
  7.     @recordCount        INT    OUTPUT,            -- 记录总数 
  8.     @strOrderBy            NVARCHAR(4000),        -- 排序 
  9.     @strWhere            NVARCHAR(4000)        -- 查询条件 
  10. AS 
  11. BEGIN 
  12.     SET NOCOUNT ON 
  13.     DECLARE @strSQL1    NVARCHAR(4000)        -- SQL语句1 
  14.     DECLARE @strSQL2    NVARCHAR(4000)        -- SQL语句2 
  15.  
  16.     -- 创建临时表 
  17.     -- 用来保存表的编号以及主键 
  18.     CREATE TABLE #Temp_Paging 
  19.     ( 
  20.         Temp_Paging_Id INT
  21.         RowNumber INT 
  22.     ) 
  23.     SET @strSQL1 = 'INSERT INTO [#Temp_Paging](Temp_Paging_Id, RowNumber) SELECT ' + @PrimaryKey + ', ROW_NUMBER() OVER (' + @strOrderBy + ') AS RowNumber FROM ' + @SqlDataTable + ' ' + @strWhere  
  24.     EXEC SP_EXECUTESQL @strSQL1 
  25.     SET @recordCount = @@ROWCOUNT -- 取得总记录数 
  26.       
  27.     -- 判断页索引 
  28.     IF @pageIndex > @recordCount * 1.0 / @pageSize + 1.0 OR @recordCount <= @pageSize 
  29.         BEGIN 
  30.             SET @pageIndex = 1 
  31.         END 
  32.       
  33.     -- 分页查询 
  34.     SET @strSQL2 = 'SELECT ' + @Fields + ' FROM ' + @SqlDataTable + ' WHERE ' + @PrimaryKey + ' IN (SELECT Temp_Paging_Id FROM [#Temp_Paging] WHERE RowNumber BETWEEN ' + Str((@pageIndex - 1) * @pageSize + 1) + ' AND ' + Str(@pageIndex * @pageSize) + ') ' + @strOrderBy 
  35.     EXEC SP_EXECUTESQL @strSQL2 
  36.     DROP TABLE #Temp_Paging -- 删除临时表 
  37. END 

从原分页存储过程很容易看出,这里运用了临时表保存编号,然后在通过pageIndex和pageSize计算所得,进行分页。

因为这里还以主键作为查询条件,故临时表中也保存了主键值。

很显然,这里的临时表无法做到通用,因为主键的类型不一定是上面定义的INT型,也可以是其它的类型,比如:uniqueidentifier(全球唯一标识)。

这样的话,这个存储过程就碰到了问题,所以必须进行改进。

2.思路一

思路一很简单,那就把这个类型声明成一个变量,然后通过系统表获取表的主键类型,再赋给变量不就可以了吗。看起来很美妙,不妨试一试先。

我们可以插入下面一段代码:

  1. DECLARE @colType NVARCHAR(50)  --主键列类型 
  2. DECLARE @strSQL3 NVARCHAR(500) --创建临时表语句 
  3.  
  4. SET @colType = (SELECT typ.name as 数据类型 
  5.                 FROM sys.columns col  
  6.                 left join sys.types typ  
  7.                 on (col.system_type_id = typ.system_type_id AND col.user_type_id = typ.user_type_id) 
  8.                 WHERE col.object_id = (SELECT object_id FROM sys.tables WHERE name = @SqlDataTable) 
  9.                 and exists  
  10.                 ( SELECT 1 FROM sys.indexes idx  
  11.                     join sys.index_columns idxCol  
  12.                       on (idx.object_id = idxCol.object_id) 
  13.                     WHERE idx.object_id = col.object_id 
  14.                       AND idxCol.index_column_id = col.column_id  
  15.                       AND idx.is_primary_key = 1 
  16.                 )) 
  17.  
  18. SET @strSQL3 = 'CREATE TABLE #Temp_Paging 
  19.                 ( 
  20.                     Temp_Paging_Id '+ @colType+'
  21.                     RowNumber INT  
  22.                 )' 
  23. PRINT @strSQL3 
  24. --EXEC(@strSQL3) 

打印结果:

  1. CREATE TABLE #Temp_Paging 
  2.                 ( 
  3.                     Temp_Paging_Id uniqueidentifier, 
  4.                     RowNumber INT  
  5.                 ) 

很显然我们得到了所需要的临时表。这时我很开心,因为得到了我想要的东西。但似乎还没有结束,我要执行下该存储过程。

坏结果往往在这时出现:

这里就出现奇怪的事了,按打印出来的明明是正确的创建临时表语句,而且也执行了,为什么接下来对临时表的操作又是无效的了?

找资料问同事,终于明白,原来临时表分本地临时表和全局临时表。本地临时表需要注意实际删除的时间。

这里说得简单一点:当在用EXEC(@strSQL3) 创建临时表的时候,同时已经删除了临时表。因为EXEC这个过程的会话已经结束,临时表就被删除了。

这里有一篇博文做了更具体的解释,大家可以参考:点滴在心头_SQL临时表

#p#

3.思路二

上面的思路似乎行不通,那不如再换个思路,干脆不要建立临时表,用Select * from (select * from table) as temptable 这种思路代替创建临时表。

代码如下:

  1. ALTER PROCEDURE [dbo].[sp_Sql_Paging] 
  2.     @SqlDataTable        NVARCHAR(4000),        -- 表名 
  3.     @PrimaryKey            NVARCHAR(4000),        -- 主键名称 
  4.     @Fields                NVARCHAR(4000),        -- 要返回的字段 
  5.     @pageSize            INT,                -- 页尺寸 
  6.     @pageIndex            INT,                -- 页码 
  7.     @recordCount        INT    OUTPUT,            -- 记录总数 
  8.     @strOrderBy            NVARCHAR(4000),        -- 排序 
  9.     @strWhere            NVARCHAR(4000)        -- 查询条件 
  10. AS 
  11. BEGIN 
  12.     SET NOCOUNT ON 
  13.     DECLARE @strSQL1    NVARCHAR(4000)        -- SQL语句1 
  14.     DECLARE @strSQL2    NVARCHAR(4000)        -- SQL语句2 
  15.     DECLARE @strSQL3    NVARCHAR(4000)        -- SQL语句3 
  16.  
  17.     SET @strSQL1 = 'SELECT ' + @PrimaryKey + ', ROW_NUMBER() OVER (' + @strOrderBy +  ') AS RowNumber FROM ' + @SqlDataTable + ' ' + @strWhere  
  18.      
  19.     --获取总记录数 
  20.     SET @strSQL3 = 'SELECT @recordCount = COUNT(*) FROM ' + @SqlDataTable + ' ' + @strWhere 
  21.     EXEC SP_EXECUTESQL  
  22.             @stmt = @strSQL3, 
  23.             @params = N'@recordCount AS INT OUTPUT'
  24.             @recordCount = @recordCount OUTPUT 
  25.  
  26.     --分页查询 
  27.     IF @pageIndex > @recordCount * 1.0 / @pageSize + 1.0 OR @recordCount <= @pageSize 
  28.         BEGIN 
  29.             SET @pageIndex = 1 
  30.         END 
  31.     SET @strSQL2 = 'SELECT ' + @Fields + ' FROM ' + @SqlDataTable + ' WHERE ' + @PrimaryKey + ' IN (SELECT '+@PrimaryKey+' FROM ('+@strSQL1+') TempTable WHERE RowNumber BETWEEN ' + Str((@pageIndex - 1) * @pageSize + 1) + ' AND ' + Str(@pageIndex * @pageSize) + ') ' + @strOrderBy 
  32.     EXEC SP_EXECUTESQL @strSQL2 
  33. END 

这里有个小知识点,注意EXEC SP_EXECUTESQL的写法以及和EXEC(@strsql)的区别。大家可以去找资料了解下。

关于分页的一些事就写到这了,仅供参考。

原文链接:http://www.cnblogs.com/willpan/archive/2011/10/10/Page.html

【编辑推荐】

  1. 常见数据库的分页实现方案
  2. Oracle分页小谈
  3. 谈谈Java调用SQL Server分页存储过程
  4. 浅述asp.net海量分页数据存储过程
  5. 证明DataReader分页的可行性

 

责任编辑:艾婧 来源: WILLPAN的博客
相关推荐

2010-09-13 13:12:28

sqlserver分页

2010-10-26 14:50:11

oracle存储过程

2010-11-10 15:16:14

Sql Server分

2010-11-29 09:45:30

Sybase分页

2009-09-17 15:22:38

LINQ to SQL

2011-03-28 10:46:36

sql server存储分页

2010-11-29 09:12:46

sybase分页存储过

2011-03-24 13:38:47

SQL Server 存储分页

2011-04-13 09:45:15

Oracle存储

2009-03-25 10:48:08

存储银行Oracle

2010-11-16 14:30:32

Oracle存储过程

2010-09-14 10:16:57

sql server

2010-09-14 10:47:45

sql server存

2012-04-23 15:10:18

ASP.NET

2010-06-10 12:37:27

MySQL分页查询

2011-06-17 17:37:16

JavaSQL Server

2011-03-24 13:31:35

2分法存储过程分页

2015-08-25 11:17:13

OpenStack对象存储Swift

2010-05-05 09:18:53

Oracle创建存储过

2010-04-30 08:47:22

Oracle分页存储
点赞
收藏

51CTO技术栈公众号