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

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

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分页的可行性

 

文章来源网络,作者:管理,如若转载,请注明出处:https://shuyeidc.com/wp/299712.html<

(0)
管理的头像管理
上一篇2025-05-23 12:58
下一篇 2025-05-23 12:59

相关推荐

  • 云服务器和云虚拟主机怎么选?云服务器和虚拟主机区别

    云服务器适合业务增长快、需弹性扩展的场景,而云虚拟主机适合预算有限、技术门槛低的小型静态网站或测试环境,二者核心区别在于资源独享性与运维复杂度,核心差异解析:从底层架构到使用体验很多人容易混淆这两者,觉得它们都是“买空间建站”,它们的底层逻辑完全不同,云服务器(ECS)就像是你租了一整栋别墅,水电网络独立,你想……

    2026-06-29
    0
  • 赣州智慧旅游招聘是真的吗?赣州旅游人才招聘信息

    中级岗位(3-5年经验)月薪范围通常在6000-10000元,这类岗位需要独立负责项目模块,如独立运营一个抖音账号,或维护一个景区小程序的功能迭代,具备成功案例的候选人议价能力较强,高级岗位(5年以上经验)月薪范围通常在10000-20000元,部分核心管理岗可达更高,这类人才需要具备战略规划能力,如制定整个景……

    2026-06-29
    0
  • 赣州智能物联网车位锁如何管理?智能车位锁管理系统多少钱

    赣州智能物联网车位锁管理的核心在于通过云端平台实现远程控锁、状态实时监控及自动计费,彻底解决传统车位“被占难管”与“找位难”的痛点,在赣州这样的城市,随着机动车保有量的持续增长,老旧小区、商业综合体以及私人固定车位的资源矛盾日益凸显,传统的机械地锁或简易遥控锁,不仅操作繁琐,更无法实现数据化管理,引入智能物联网……

    2026-06-29
    0
  • 赣州智能消防栓好用吗,智能消防栓多少钱一个

    赣州智能消防栓通过物联网技术实现实时监测与远程报警,能显著降低火灾响应时间并提升城市消防安全管理水平,是目前智慧城市建设中不可或缺的基础设施,赣州智能消防栓的核心价值与应用场景传统消防栓往往存在“看不见、摸不着、用不了”的痛点,在赣州这样地形复杂、老城区与新城区并存的区域,传统设施的管理难度极大,智能消防栓的出……

    2026-06-29
    0
  • 云服务器和物理机到底有啥区别?

    云服务器本质上是虚拟化资源池中的弹性实例,而传统物理服务器是独占的硬件实体,前者胜在弹性与运维便捷,后者强在物理隔离与性能稳定,具体选择取决于业务对成本、扩展性及安全合规的权衡,很多人初次接触服务器时,容易把“云服务器”和“传统物理服务器”混为一谈,觉得它们都是用来跑网站或存数据的盒子,这两者的底层逻辑完全不同……

    2026-06-29
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注