数据分页获取(二)

        搞了一上午,终于把Oracle 11g R2装上了,新建了数据库、表,也将实验所需的数据添加上了(PS:伤不起的R2,创建了一个实例后,我5K的台式机在实例启动时都差点死掉,启动都将近用了三五分钟~ ,做完实验还是把它给干掉吧!伤不起...
 
         在“数据分页获取(一)” 中,讲了SQL Server平台(SQL Server 2005及更高版本)下的数据分页查询(获取)的技巧,并在文章的最后提到了Oracle平台下的数据分页查询,本文就对Oracle平台下的数据分页技术来好好讲解一下,我也只讲解其中一种。

        “数据分页获取(一)” 的文末提到过,Oracle提供了一个rownum关键字,此关键字集SQL Server中的ROW_NUMBER()函数、Top子句的功能于一身。利用此关键字,可以在 select 子句对结果集进行投影时,为最后的结果集附加一个rownum列(也就是为结果集给出行号)。有了行号,分页就简单了!

        由于要让Oracle的存储过程返回结果集并不像SQL Server那样简单,而是有一定复杂度的,所以我还是用最简单的方法——拼接SQL语句来讲解一下rownum关键字在分页查询中的应用(在真实的开发中也可以进行SQL拼接再传入数据库引擎执行,但要注意SQL注入攻击)!先来看看实验结果吧,如下三张截图所示:
 

     看到上图,因为我没有为表设置主键,也没有为表建任何其他的索引,所以耗时达到了0.016秒。有索引的话应当会更快的!
 
     第一条 Select 命令是为了统计符合条件的总计录数的,这在真实的开发中是有一定作用的,比如用来展示给客户看的列表下边一般都会给出符合条件的总记录数的。
 
     第二条 Select 命令才是真正的分页命令!可以看到它用到了嵌套子查询。不知道各位有没有想过,为什么要使用嵌套子查询,而不直接用 select rownum as N, xs.* from xs where xs.专业='信息管理' and rownum between (2-1)*6+1 and 2*6 呢?会这样问就很好!如果你还没找到原因,那现在我来告诉你吧!原因就在这个 rownum 关键字的内部工作机制上。
 
       rownum 关键字可以为查询返回的结果集添加一个称为“行(序)号”的列,也就是 rownum 列,因为 rownum 列不是真实物理地存在于相应表中的,而是由 rownum 关键字在返回的结果集上动态地生成添加上去的,因此 rownum 列也称为“伪列(pseudo-column)”。说到伪例(pseudo-column)就不得不提到Oracle中另一个有名的伪列——rowid。rowid 是Oracle系统内部用于定位和查找存储在表中的数据行唯一标识ID,该ID是由Oracle收购并继续开发的TimesTen(一种内存数据库服务)动态指定的。rowid 列的数据类型就是ROWID(不要惊讶,因为ROWID是Oracle众多数据类型中的一种!)。因为伪列rowid不是真实存在的列,所以它不需要占用数据库的物理空间,也不能修改、查找或者删除。下面是一段Oracle官方关于ROWID的概要说明(做IT的应当都能看懂):
 
ROWID
TimesTen assigns a unique ID called a rowid to each row stored in a table. The rowid has data type ROWID. You can examine a rowid by querying the ROWID pseudocolumn.

Because the ROWID pseudocolumn is not a real column, it does not require database space and cannot be updated, indexed or dropped.

The rowid value persists throughout the life of the table row, but the system can reassign the rowid to a different row after the original row is deleted. Zero is not a valid value for a rowid.

Rowids persists through recovery, backup and restore operations. They do not persist through replication, ttMigrate or ttBulkCp operations.

       好了,回到rownum 关键字!rownum 关键字是从1开始,依次递增地为结果集动态指定行号的,而且rownum 关键字的工作时机是在where中列出的其他条件之后,Order by 子句之前(这一点要注意的)。“从1开始,依次递增地为结果集动态指定行号”,这就是为什么不能直接用前面提到的“select rownum as N, xs.* from xs where xs.专业='信息管理' and rownum between (2-1)*6+1 and 2*6”进行分页查询的原因(结果集出来后才生成的行号,已经失去了分页的时机)。你不能让rownum关键字实现从7开始为结果集动态指定行号到12这样的行为,你也不能让rownum关键字实现从2开始为结果集动态指定行号这样的行为(即:select rownum as N, xs.* from xs where xs.专业='信息管理' and rownum>2 也是不行的),因为rownum关键字是从1开始为结果集指定行号的。也就是说你没办法指定rownum关键字为结果集动态指定行号时的起始线(它一直都是从1开始的),下面的语句都是不能通过的:

select rownum as N, xs.* from xs where xs.专业='信息管理' and rownum>1;

select rownum as N, xs.* from xs where xs.专业='信息管理' and rownum>=2;

select rownum as N, xs.* from xs where xs.专业='信息管理' and rownum>5;

select rownum as N, xs.* from xs where xs.专业='信息管理' and rownum=10;这种也是不行的,因为rownum关键字是用来动态指定行号的,你不能让它静态的等于某个值。

但是你可以指定rownum关键字结果集动态指定行号时终止线,下面的语句都是能通过的:

select rownum as N, xs.* from xs where xs.专业='信息管理' and rownum<6;

select rownum as N, xs.* from xs where xs.专业='信息管理' and rownum<=10;

       我们再看到“select T.学号,T.姓名,T.专业 from (select rownum as N, xs.* from xs where xs.专业='信息管理' and rownum<1*6) T where T.N>=((2-1)*6+1) ”,这里外层查询所用的T.N是由内层子查询的select子句投影伪列rownum而来的。经投影后,在内层了查询生成的中间结果中,原来的伪列rownum已经转化为普通的列了,外层查询就可以用此列来做更精确的分页界定。


最后,我们来看几段Oracle官方关于ROWNUM的概述(同样,做IT的应当都能看懂):

ROWNUM

For each row returned by a query, the ROWNUM pseudocolumn returns a number indicating the order in which the row was selected. The first row selected has a ROWNUM of 1, the second a ROWNUM of 2, and so on.

Use ROWNUM to limit the number of rows returned by a query as in this example:

SELECT * FROM employees WHERE ROWNUM < 10;

The order in which rows are selected depends on the index used and the join order. If you specify an ORDER BY clause, ROWNUM is assigned before sorting. However, the presence of the ORDER BY clause may change the index used and the join order. If the order of selected rows changes, the ROWNUM value associated with each selected row could also change.

For example, the following query may return a different set of employees than the preceding query if a different index is used:

SELECT * FROM employees WHERE ROWNUM < 10 ORDER BY last_name;

Conditions testing for ROWNUM values greater than a positive integer are always false. For example, the following query returns no rows:

SELECT * FROM employees WHERE ROWNUM > 1;

Use ROWNUM to assign unique values to each row of a table. For example:

UPDATE my_table SET column1 = ROWNUM;

If your query contains either FIRST NumRows or ROWS m TO n, do not use ROWNUM to restrict the number of rows returned. For example, the following query results in an error message:

SELECT FIRST 2 * FROM employees WHERE ROWNUM <1 ORDER BY employee_id; 2974: Using rownum to restrict number of rows returned cannot be combined with first N or rows M to N
原文地址:https://www.cnblogs.com/xuruiming/p/2859118.html