转:http://blog.csdn.net/andkylee/article/details/5638033
本篇介绍三种方法。
第一种:利用游标
程序开发人员比较喜欢使用游标,因为游标的“循环”遍历方式类似编程语言中的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
(2) 插入测试数据
(3) 循环插入大量的重复数据,
向表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
返回的结果为:
需要将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
分别看看执行结果吧。
第二种方式的结果:
当然,两种结果一模一样。
最后我们测试表testA中的ID列顺序值打乱, 来看看以上语句的执行情况。执行:
ID列值打乱之后,前100行的数据为:
我们要求返回满足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
结果为:
如果不对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
相应的结果集为:
可以发现这个两句的结果是不同的。
我想既然都要求返回指定范围的结果集, 肯定是有排序的依据了, 否则怎么知道该返回哪个范围呢?
还有,我给出的第三种方法,在进行表扫描的时候,即使不指定排序,也是能够得到正确结果的。因为表扫描时很可能会按照表内数据在物理页面上的物理位置来返回结果。