`
123003473
  • 浏览: 1064691 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

sybase分页过程

 
阅读更多
1、用存储过程,建立临时表,获取数据,然后动态sql获得临时表数据。最后从网上找到的如下的两个分页存储过程:
-- 按行读取

CREATE PROCEDURE GetDataByLine
(
        --创建一个分页读取过程
    @SqlStr         varchar(8000),    --SQL语句
    @FirstRec       int,        --页起始行
    @LastRec        int        --页结束行
)
AS
DECLARE @dt varchar(10)    --生成临时表的随机数
BEGIN

    SELECT @dt= substring(convert(varchar, rand()), 3, 10)    --一个字符型的随机数
    
    --将搜索结果放入临时表中,表名随机生成,在' FROM '前插入'INTO '+随机临时表名
    SELECT @SqlStr = stuff(@SqlStr, charindex(' FROM ', upper(@SqlStr)), 6 ,' INTO tempdb..Lining' + @dt + ' FROM ')
    EXECUTE (@SqlStr)
    
    --为临时表增加id号
    SELECT @SqlStr = 'ALTER TABLE tempdb..Lining' + @dt + ' ADD TEMPDB_ID numeric(10) IDENTITY PRIMARY KEY'
    EXECUTE (@SqlStr)
    
    --计算临时表中的记录数
    --SELECT @SqlStr = 'SELECT Count(*) From tempdb..Lining' + @dt
    --EXECUTE (@SqlStr)
    
    --选取记录号在起始行和结束行中间的记录
    SELECT @SqlStr = 'SELECT * FROM tempdb..Lining'+@dt+' WHERE TEMPDB_ID > ' + convert(varchar, @FirstRec) + ' and TEMPDB_ID <= ' + convert(varchar, @LastRec)
    EXECUTE (@SqlStr)
    
    --删除临时表
    SELECT @SqlStr = 'DROP TABLE tempdb..Lining'+@dt
    EXECUTE (@SqlStr) 
  
END

/*
some comments:
1.@SqlStr     varchar(8000), depends on your page size
2. this is a generic paging sp, if you just want to use it for specific table, 
   you'd better change the 'tempdb..Lining' to #Paging, the performance will be better
*/

-- 按页读取

CREATE PROCEDURE GetDataByPage
(
    --创建一个分页读取过程
    @SqlStr         varchar(8000),    --SQL语句
    @PageSize       int,            --每页记录数
    @CurrentPage    int                --当前页数
)
AS
DECLARE @FirstRec int, @LastRec int, @dt varchar(10)    --页起始行,页结束行,生成临时表的随机数
BEGIN

    SELECT @FirstRec = (@CurrentPage - 1) * @PageSize    --计算页起始行
    SELECT @LastRec = (@CurrentPage * @PageSize + 1)    --计算页结束行
    
    SELECT @dt= substring(convert(varchar,rand()),3,10)    --一个字符型的随机数
    
    --将搜索结果放入临时表中,表名随机生成,在' FROM '前插入'INTO '+随机临时表名
    SELECT @SqlStr = stuff(@SqlStr, charindex(' FROM ',upper(@SqlStr)), 6 ,' INTO tempdb..Paging'+@dt+' FROM ')
    EXECUTE (@SqlStr)
    
    --为临时表增加id号
    SELECT @SqlStr = 'ALTER TABLE tempdb..Paging'+@dt+' ADD TEMPDB_ID numeric(10) IDENTITY PRIMARY KEY'
    EXECUTE (@SqlStr)
    
    --计算临时表中的记录数
    --SELECT @SqlStr = 'SELECT Count(*) From tempdb..Paging'+@dt
    --EXECUTE (@SqlStr)
    
    --选取记录号在起始行和结束行中间的记录
    SELECT @SqlStr = 'SELECT * FROM tempdb..Paging'+@dt+' WHERE TEMPDB_ID > '+convert(varchar,@FirstRec)+' and TEMPDB_ID < '+convert(varchar,@LastRec)
    EXECUTE (@SqlStr)
    
    --删除临时表
    SELECT @SqlStr = 'DROP TABLE tempdb..Paging'+@dt
    EXECUTE (@SqlStr) 
  
END

/*
some comments:
1. @SqlStr     varchar(8000), depends on your page size
2. this is a generic paging sp, if you just want to use it for specific table, 
   you'd better change the 'tempdb..Paging' to #Paging, the performance will be better
*/



一段很好的分页程序,速度很快,sybase内部员工写的,改成自己需要的SQL吧。

create procedure test_p @ipage int, @num int as   /* @ipage  页码, @num 每页的记录数 */ 
begin 
declare @maxpages int, @rcount int   /* @maxpages 最大页码 */ 
if @ipage>=100 
select @maxpages=ceiling(count(*)/@num) from test 
else 
         select @maxpages=100000 
if @ipage<=@maxpages/2  
begin 
select @rcount=@ipage*@num 
set rowcount @rcount 
select id=identity(12),name,descs,ddd into #temptable1 from test order by id 
select * from #temptable1  where id>=(@ipage-1)*@num and id<= @ipage*@num 
end else 
begin 
select @rcount=(@maxpages-@ipage+1)*@num 
set rowcount @rcount 
select id=identity(12),name,descs,ddd into #temptable2 from test order by id desc  
select id,name, ddd,descs from #temptable2  where id>=(@maxpages-@ipage)*@num and id<= (@maxpages-@ipage+1)*@num  order by id  desc 
end 
end



好事做到底,送个通用版吧

create procedure splitpage @qry varchar(16384),@ipage int, @num int as   /*@qry SQL语句, @ipage 页数, @num 每页记录条数 */ 
begin 
        declare @maxpages int 
        declare @rcount int 
        declare @execsql varchar(16384) 

        if @ipage>=100 
                select @maxpages=ceiling(count(*)/@num) from test 
        else 
                select @maxpages=100000 
        if @ipage<=@maxpages/2 
        begin 
                select @rcount=@ipage*@num 
                set rowcount @rcount 
                set @execsql = stuff(@qry,charindex('select',@qry),6,'select sybid=identity(12),') 
                set @execsql = stuff(@execsql, charindex('from',@execsql),4,'into #temptable1 from') 
                set @execsql = @execsql || ' select * from #temptable1  where sybid>' || convert(varchar,(@ipage-1)*@num) || ' and sybid <= ' || convert(varchar,@ipage*@num) 
                execute (@execsql) 

        end else 
        begin 
                select @rcount=(@maxpages-@ipage+1)*@num 
                set rowcount @rcount 
                set @execsql = stuff(@qry,charindex('select',@qry),6,'select sybid=identity(12),') 
                set @execsql = stuff(@execsql, charindex('from',@execsql),4,'into #temptable1 from') 
                set @execsql = @execsql || ' order by sybid desc' 
                set @execsql = @execsql || ' select * from #temptable1 where sybid > ' || convert(varchar,(@maxpages-@ipage)*@num) || ' and sybid <= ' || convert(varchar,(@maxpages-@ipage+1)*@num) 
                execute (@execsql) 
        end 
end


本篇介绍三种方法。

第一种:利用游标

程序开发人员比较喜欢使用游标,因为游标的“循环”遍历方式类似编程语言中的for,while,loop语句的实现方法,写起来比较容易。使用游标一般步骤是:为指定的SQL语句定义一个游标,打开并移动游标,当移动到指定行号的记录行之后,再按照需要提取的行数来取数据。从表面上看解决了提取指定范围数据的问题;但是在实际应用 上,有可能会出现严重的性能问题。建立游标需要耗用一定的系统资源之外;当表内的数据量有上千万甚至到亿级别并且需要取大量的数据结果时,用游标每移动一次就取这行数据,然后再移动游标,这个过程将是缓慢的。在使用游标的过程中,系统会给相应的表加上共享锁,导致锁竞争而严重影响数据库的性能。

在此不再介绍游标的实现方式,此法比较简单。

第二种:利用临时表和标志列

在Sybase ASE12.5.3及以后的版本中,我们可以用top关键字来限定只返回结果集的前N行数据。在ASE12.5.3之前的版本中只能用set rowcount N 的方法来“曲线救国”了。

对于取结果集的第N行至第N+M行数据的要求,我们考虑利用top来实现的话,比较容易想到的是:执行两次top,再加l两次倒序排序。

步骤如下:

       (1) select top N+M * from table_name where_clause order by ID     把此结果集派生为表:table_name1

                  (2)   select top M * from table_name1 order by ID  DESC   把此结果集派生为表:table_name2

                  (3)   select * from table_name2 order by ID  DESC

上面的3条语句好像能够实现返回第N行至第N+M行数据的要求。但是,在Sybase ASE中仅仅利用派生表而不利用临时表是不能实现这个要求的。

仅仅是ASE中的“派生出派生表(derived table)的SQL语句中不能含有order by 子句”这个限制就足以使上面的方法行不通。还有一个限制是,上面的3个步骤中都利用ID列进行排序。如果表中没有可用的排序列时,那么上述方法也不能用了。不过幸运的是,一般要求对其结果集进行分页的表都是有可以用作排序的列的(数字型或者日期型)。

继续寻找一个能用的方法,下面着重介绍目前通用的ASE的分页思路。此思路的关键是产生identity自增列和临时表。

在ASE中大家要是找到了不用临时表就可以实现分页的方法请麻烦告诉我一声。 我尝试了很多次,都不是很理想。

概括起来主要语句有两条:

           (1)   select syb=identity(10),*  into #temp_table from table_name where_clause   order_by_clause

           (2)   select  * from #temp_table where_clause and syb >= N   and syb <= N+M

用一个例子演示一下:

(1) 建立测试表:testA

create table testA(id int not null,name varchar(30) null) go


(2) 插入测试数据

insert into testA select 1,'liuzhenfu' go insert into testA select 2,'andkylee' go


(3) 循环插入大量的重复数据,

insert into testA select id+(select max(id) from testA),name from testA go 15


向表testA循环插入已有的数据,15次之后,表testA内的数据达到2^16 = 65536 行。

(4) 利用临时表 + 自增标志列来提取第100行至第200行的数据。

语句如下:

select syb=identity(10) ,* into #tempA from testA

select * from #tempA where syb>=100 and syb<=200

drop table #tempA

返回的结果为:

1> select syb=identity(10),* into #tempA from testA 2> select * from #tempA where syb>=100 and syb<=200 3> go (65536 rows affected) syb id name ------------- ----------- --------------------------- 100 100 andkylee 101 101 liuzhenfu 102 102 andkylee 103 103 liuzhenfu 104 104 andkylee 105 105 liuzhenfu 106 106 andkylee 107 107 liuzhenfu 108 108 andkylee 109 109 liuzhenfu 110 110 andkylee 111 111 liuzhenfu 112 112 andkylee 113 113 liuzhenfu 114 114 andkylee 115 115 liuzhenfu 116 116 andkylee 117 117 liuzhenfu 118 118 andkylee 119 119 liuzhenfu 120 120 andkylee 121 121 liuzhenfu 122 122 andkylee 123 123 liuzhenfu 124 124 andkylee 125 125 liuzhenfu 126 126 andkylee 127 127 liuzhenfu 128 128 andkylee 129 129 liuzhenfu 130 130 andkylee 131 131 liuzhenfu 132 132 andkylee 133 133 liuzhenfu 134 134 andkylee 135 135 liuzhenfu 136 136 andkylee 137 137 liuzhenfu 138 138 andkylee 139 139 liuzhenfu 140 140 andkylee 141 141 liuzhenfu 142 142 andkylee 143 143 liuzhenfu 144 144 andkylee 145 145 liuzhenfu 146 146 andkylee 147 147 liuzhenfu 148 148 andkylee 149 149 liuzhenfu 150 150 andkylee 151 151 liuzhenfu 152 152 andkylee 153 153 liuzhenfu 154 154 andkylee 155 155 liuzhenfu 156 156 andkylee 157 157 liuzhenfu 158 158 andkylee 159 159 liuzhenfu 160 160 andkylee 161 161 liuzhenfu 162 162 andkylee 163 163 liuzhenfu 164 164 andkylee 165 165 liuzhenfu 166 166 andkylee 167 167 liuzhenfu 168 168 andkylee 169 169 liuzhenfu 170 170 andkylee 171 171 liuzhenfu 172 172 andkylee 173 173 liuzhenfu 174 174 andkylee 175 175 liuzhenfu 176 176 andkylee 177 177 liuzhenfu 178 178 andkylee 179 179 liuzhenfu 180 180 andkylee 181 181 liuzhenfu 182 182 andkylee 183 183 liuzhenfu 184 184 andkylee 185 185 liuzhenfu 186 186 andkylee 187 187 liuzhenfu 188 188 andkylee 189 189 liuzhenfu 190 190 andkylee 191 191 liuzhenfu 192 192 andkylee 193 193 liuzhenfu 194 194 andkylee 195 195 liuzhenfu 196 196 andkylee 197 197 liuzhenfu 198 198 andkylee 199 199 liuzhenfu 200 200 andkylee (101 rows affected)


需要将select * from #tempA中的星号*替换为需要返回的列名。

继续。。。。

当要求返回满足name='andkylee'的所有行中的第100行至第200行的数据时, 利用

select syb=identity(10),* into #tempA from testA where name='andkylee'

select * from #tempA where syb>=100 and syb<=200


drop table #tempA

第三种:利用rowcount

此种方法有点不足:必须利用可用作排序的列 对结果集进行排序。

还是上面的测试表testA,如果从第9000行开始选择10行数据,那么语句如下:

declare @id1 int
set rowcount 9000
select @id1 = id from testA order by id
set rowcount 10
select *from testA where id >= @id1 order by id
set rowcount 0
go

此种方法中核心语句是select @id1=id from testA order by id , 在对表testA执行查询的过程中,每读取一行都会把id列的值赋给@id1这个变量,一直持续到最后一行,@id1这个变量反复被下一行的id值刷新,结果只得到最后一样的id值。如果在此select语句之前加上rowcount的限定,那么就可用使得@id1这个变量获得第rowcount行的id值,那么我们也就获得了返回范围结果集的起点了。

后面的 set rowcount 10

         select * from testA where id >= @id1 order by id

这两句实际上可以用一句select top 10 * from testA where id >= @id1 order by id  来替代。

这样,两种不同的实现形式为:

declare @id1 int
set rowcount 9000
select @id1 = id from testA  order by id
set rowcount 0
select top 10  *from testA where  id >= @id1 order by id
go

分别看看执行结果吧。

1> declare @id1 int 2> set rowcount 9000 3> select @id1 = id from testA order by id 4> set rowcount 10 5> select *from testA where id >= @id1 order by id 6> set rowcount 0 7> go (9000 rows affected) id name ----------- ------------------------------ 9000 andkylee 9001 liuzhenfu 9002 andkylee 9003 liuzhenfu 9004 andkylee 9005 liuzhenfu 9006 andkylee 9007 liuzhenfu 9008 andkylee 9009 liuzhenfu (10 rows affected) 1>


第二种方式的结果:

1> declare @id1 int 2> set rowcount 9000 3> select @id1 = id from testA order by id 4> set rowcount 0 5> select top 10 *from testA where id >= @id1 order by id 6> go (9000 rows affected) id name ----------- ------------------------------ 9000 andkylee 9001 liuzhenfu 9002 andkylee 9003 liuzhenfu 9004 andkylee 9005 liuzhenfu 9006 andkylee 9007 liuzhenfu 9008 andkylee 9009 liuzhenfu (10 rows affected) 1>


当然,两种结果一模一样。

最后我们测试表testA中的ID列顺序值打乱, 来看看以上语句的执行情况。执行: 

update testA set id = id + cast( rand() * 65536 as int )


ID列值打乱之后,前100行的数据为:

1> select top 100 * from testA 2> go id name ----------- ------------------------------ 51366 liuzhenfu 33573 andkylee 19447 liuzhenfu 19408 andkylee 57839 liuzhenfu 18817 andkylee ...................... 19075 liuzhenfu 17081 andkylee 26444 liuzhenfu 6620 andkylee 52344 liuzhenfu 49348 andkylee (100 rows affected)


我们要求返回满足name='andkylee'的从第9000行开始的10行数据。

declare @id1 int
set rowcount 9000
select @id1 = id from testA where name='andkylee' order by id
set rowcount 10
select *from testA where name='andkylee' and id >= @id1 order by id
set rowcount 0
go

结果为:

1> declare @id1 int 2> set rowcount 9000 3> select @id1 = id from testA where name='andkylee' order by id 4> set rowcount 10 5> select *from testA where name='andkylee' and id >= @id1 order by id 6> set rowcount 0 7> go (9000 rows affected) id name ----------- ------------------------------ 48639 andkylee 48639 andkylee 48641 andkylee 48641 andkylee 48642 andkylee 48643 andkylee 48644 andkylee 48644 andkylee 48650 andkylee 48650 andkylee (10 rows affected)


如果不对ID列进行排序, 有下面的sql语句:

declare @id1 int
set rowcount 9000
select @id1 = id from testA where name='andkylee'
set rowcount 10
select *from testA where name='andkylee' and id >= @id1 
set rowcount 0
go

相应的结果集为:

1> declare @id1 int 2> set rowcount 9000 3> select @id1 = id from testA where name='andkylee' 4> set rowcount 10 5> select *from testA where name='andkylee' and id >= @id1 6> set rowcount 0 7> go (9000 rows affected) id name ----------- ------------------------------ 74076 andkylee 74514 andkylee 74053 andkylee 74385 andkylee 74339 andkylee 74792 andkylee 74794 andkylee 74984 andkylee 75052 andkylee 74138 andkylee (10 rows affected) 1>


可以发现这个两句的结果是不同的。

我想既然都要求返回指定范围的结果集, 肯定是有排序的依据了, 否则怎么知道该返回哪个范围呢?

还有,我给出的第三种方法,在进行表扫描的时候,即使不指定排序,也是能够得到正确结果的。因为表扫描时很可能会按照表内数据在物理页面上的物理位置来返回结果。

就先介绍到这里吧, 后续可能会根据情况进行补充。


分享到:
评论

相关推荐

    sybase 实现分页的存储过程

    ### Sybase 中实现分页技术的存储过程 #### 背景介绍 Sybase 是一款高性能的关系型数据库管理系统,广泛应用于金融、电信等对数据处理性能有极高要求的领域。在实际的应用场景中,常常需要从数据库中获取大量的数据...

    sybase分页存储过程(代码)

    sybase分页存储过程(代码),分页存储过程代码,实现sybase数据库分页查询。

    sybase分页代码使用jdbc分页速度快适合大数据

    Java中的分页实现通常有两种方法:一种是使用存储过程,另一种是使用纯Java代码。这里我们讨论的是后者,因为它更灵活且无需额外的数据库对象。使用Java代码实现分页的关键在于维护一个游标(cursor)和一个缓存...

    sybase通用分页模块

    3. **代码示例**:提供具体的SQL查询示例,展示如何在Sybase中编写分页查询语句,可能还会包括存储过程或函数的实现。 4. **工具支持**:介绍可能使用的开发工具,如SQL客户端、IDE等,以及如何在这些工具中执行...

    sybase分页优化带排序

    总之,Sybase分页优化带排序是一个涉及多方面技术的问题,包括选择合适的分页方法、利用窗口函数、优化索引、使用存储过程等。在实际操作中,应根据具体的数据规模、查询模式和系统资源来制定最佳策略。通过这些方法...

    sybase sqlanywhere5.0

    SQL Anywhere 5.0是由Sybase公司开发的一款轻量级数据库管理系统,它以其强大的移动和嵌入式数据库解决方案而闻名。在中石油等大型企业中,SQL Anywhere常用于管理各种关键业务数据,确保在分布式环境下的高效运行和...

    C#使用ODBC连接SYBASE数据库的方法

    ### C#使用ODBC连接SYBASE...综上所述,通过C#和ODBC连接Sybase数据库的过程虽然较为复杂,但只要掌握了基本步骤和技巧,就能有效地完成数据的读写操作。这对于跨平台数据交互以及旧系统改造等方面都有着重要的意义。

    SYBASE技术资料

    - **SYBASE**与**ORACLE**都支持存储过程的编写,用于封装复杂的业务逻辑或数据库操作。 **5. 函数** - **SYBASE**与**ORACLE**都支持自定义函数的创建,用于执行特定计算或逻辑处理。 **6. 同义词** - **ORACLE**...

    基于JavaBean的分页技术

    ### 基于JavaBean的分页技术详解 #### 摘要 本文将详细介绍如何利用JavaBean结合JSP(JavaServer Pages)实现数据库的分页功能...但在实际的研究或开发过程中,应详细列出所参考的所有文献资料,以便读者查阅和验证。

    Sybase IQ 15.4参考手册(性能调优)

    Sybase IQ 15.4提供了分页技术来增加可用内存,并通过监控交换的实用程序来监控内存的使用情况。服务器内存是数据库性能的关键,因此需要管理好缓冲区高速缓存的大小。缓冲区高速缓存的大小应根据数据库的实际工作...

    sybase性能调休指南

    - 查询树是查询计划的一种可视化表示形式,有助于理解查询执行的过程。 11. **使用查询计划** - 通过查询计划可以诊断和优化查询性能问题。 12. **控制查询处理** - 设置查询时间限制、查询优先级等参数可以更...

    sybase开发参考.pdf

    综上所述,本Sybase ASE开发参考为开发者提供了TSQL、ESQL/C和JDBC三个方面的优化指导,旨在帮助用户提升数据库性能,实现高效并发处理,并解决在开发过程中可能遇到的问题。通过遵循这些最佳实践,可以显著改善应用...

    sybaseIQ16性能调优指南

    SAP Sybase IQ 16性能调优指南是一份专为数据库管理员、数据库设计人员和开发人员准备的文档,旨在帮助他们配置和优化SAP Sybase IQ数据库系统,以提升其性能表现。性能调优涉及调整和配置多种系统参数,使得数据库...

    PHP Sybase CT driver enhancements-开源

    最后,**游标支持** 使开发者能够控制查询结果的检索方式,例如逐行读取或分页处理大结果集。这种方式节省了内存资源,对处理大量数据非常有用。 开源软件的特性意味着这些增强功能是免费且透明的,开发者可以自由...

    小型机重启操作方法

    这将分页显示错误报告,用于查看是否存在启动过程中产生的异常信息。 **步骤4:** 更详细的错误日志信息可以通过以下命令获得: ``` errpt -aj ``` 这将显示更详细的错误日志,包括时间戳、资源名称以及描述等信息,...

    jConnect-6_0

    8. **JDBC 3.0规范兼容**:jConnect-6_0符合JDBC 3.0标准,支持预编译语句、结果集的滚动和分页等高级功能。 9. **易用性**:提供完善的API文档和示例代码,简化开发流程,使开发者能够快速上手。 **在压缩包中的...

    SocanCode7.4.1

    支持Access,SqlServer,MySql,Oracle,SQLite,Sybase,DB2,PostgreSql连接 全模板化的代码生成器,轻松修改即可实现自定义各种编程语言模板、支持代码批量输出 内置一套强大的C#三层架构模板,此模板支持生成简单三层及...

    sql_anywhere_hibernate

    SQL Anywhere是Sybase公司提供的一种轻量级、嵌入式数据库系统,广泛用于移动设备和分布式环境。 描述提到“在hibernate中关于开发中用到的方言jar包”,方言在Hibernate中起着至关重要的作用。方言是Hibernate与...

    JTurboExplorer:MySQL,Oracle,Sybase等数据库浏览器。 来源制造商JSF。-开源

    它是帮助程序员检查数据库并如此轻松地生成代码的工具。... 从表生成存储过程以进行插入,更新,删除,查询和分页。 从表中生成代码JSF2.0(Action)和PrimeFaces(xhtml),Struts 1.3的代码,等等。

Global site tag (gtag.js) - Google Analytics