Oracle读取数据的顺序问题

Posted bisal的个人杂货铺

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Oracle读取数据的顺序问题相关的知识,希望对你有一定的参考价值。

点击标题下「蓝色微信名」可快速关注

预计阅读时间:9分钟


Oracle中,关于数据检索的顺序,可能有个误区,即“如果数据按顺序存储(insert),则可能读取(select)出来的数据就是按顺序的,即使不带order by“,所谓的“默认排序”,准确地说,无order by的语句,返回的结果顺序,应该是不确定的,而且不是“随机”。


Oracle中,默认创建的表,都是heap表,即堆表。堆表存放数据,就是“随便存“,存的时候就是乱序的。如果创建的是索引组织表,数据就会默认排序了。


一张堆表,Oracle对无order by的语句返回的结果不进行排序,此时的处理方式是按照数据的物理存储顺序来读取数据,就是说查询的结果和数据存储在磁盘上的位置有关系。



下面通过实验,进一步了解下排序的问题,首先,创建测试表空间,大小设为1M,

SQL> create tablespace test_tbs 
     datafile '/u01/app/oracle/oradata/BISAL/test_tbs01.dbf' size 1m;

Tablespace created.


创建一张测试表,test_1,定义在test_tbs表空间,初始分配空间896k,

SQL> create table test_1 (id number) 
     tablespace test_tbs storage(initial 896k);

Table created.


之所以设为896k,为了先占据空间,后面会用到,1024k-896k=128k,但是数据文件头,会占用些空间,因此可用空间,小于128k,

SQL> select 1024-896 from dual;
 1024-896
----------
      128


但是需要注意,由于11g新特性,延迟段创建,因此为了真正占用存储空间,要实际插入一条数据,此时能看出,test_tbs表空间,已被使用,

TBSPNAME  TBSP_SIZE  USED  FREE   PCT_USED
--------  --------- ------ ------ ---------
TEST_TBS      1         0   .9375      0

SQL>
 insert into test_1 values(1);
1 row created.

SQL>
 commit;
Commit complete.

TBSPNAME  TBSP_SIZE  USED   FREE   PCT_USED
--------  --------- ------ ------ ---------
TEST_TBS      1       .875  .0625     87.5


创建第二张表,test_2,包含3个char(2000)的字段,由于1个block,默认8k,因此1条记录,会占用1个block,同时设置initial初始分配空间64k,刚才说了,此时表空间,<128k了,因此最多分配1次,

SQL> create table test_2 (id number, a char(2000), b char(2000), 
     c char(2000)) tablespace test_tbs storage(initial 64k);
Table created.


我们计算一下,1个block是8k,表头占据3个block,1条记录占据1个block,1个extent是64k,因此1个extent只能存储五条记录,再次分配空间,会提示空间已满,

SQL> insert into test_2 values(1'A''A''A');
1 row created.

SQL> insert into test_2 values(2'A''A''A');
1 row created.

SQL> insert into test_2 values(3'A''A''A');
1 row created.

SQL> insert into test_2 values(4'A''A''A');
1 row created.

SQL> insert into test_2 values(5'A''A''A');
1 row created.

SQL> insert into test_2 values(6'A''A''A');
insert into test_2 values(6'A''A''A')
           *
ERROR at line 1:
ORA-01653: unable to extend table BISAL.TEST_2 by 8 in tablespace TEST_TBS


返回的结果,并不是按照插入顺序显示,而是按照rowid的顺序,

SQL> select id, rowid from test_2;
 ID ROWID
---------- ------------------
  2 AAADr1AAHAAAAB7AAA
  3 AAADr1AAHAAAAB8AAA
  4 AAADr1AAHAAAAB9AAA
  5 AAADr1AAHAAAAB+AAA
  1 AAADr1AAHAAAAB/AAA
 
5 rows selected.


现在删除test_1,腾出空间,向test_2插入第6条记录,

SQL> drop table test_1 purge;
Table dropped.

SQL>
 insert into test_2 values(6, 'A''A''A');
1 row created.


再看返回的结果,由于test_1空间释放,test_2可以分配第2个64k的extent,而他的block_id要小于原extent的block_id,因此这条记录的rowid,要比前5条记录rowid小,所以,现在返回的结果,又不是按照rowid排序,准确地说,同一个extent中,Oracle会按照rowid顺序读取,否则,就不一定了,

SQL> select id, rowid from test_2;
 ID ROWID
---------- ------------------
  2 AAADr1AAHAAAAB7AAA
  3 AAADr1AAHAAAAB8AAA
  4 AAADr1AAHAAAAB9AAA
  5 AAADr1AAHAAAAB+AAA
  1 AAADr1AAHAAAAB/AAA
  6 AAADr1AAHAAAAAJAAA
6 rows selected.


补充一下,如果需要检索结果的顺序,是随机的,此时能用dbms_random包,构造随机数,order by一个随机数,结果就是随机的,如下所示,两次select结果的顺序,是不同的,

SQL> select id, rowid from test_2 order by dbms_random.value;
 ID ROWID
---------- ------------------
  3 AAADr1AAHAAAAB8AAA
  6 AAADr1AAHAAAAAJAAA
  5 AAADr1AAHAAAAB+AAA
  1 AAADr1AAHAAAAB/AAA
  2 AAADr1AAHAAAAB7AAA
  4 AAADr1AAHAAAAB9AAA
6 rows selected.

SQL> select id, rowid from test_2 order by dbms_random.value;
 ID ROWID
---------- ------------------
  3 AAADr1AAHAAAAB8AAA
  6 AAADr1AAHAAAAAJAAA
  2 AAADr1AAHAAAAB7AAA
  1 AAADr1AAHAAAAB/AAA
  5 AAADr1AAHAAAAB+AAA
  4 AAADr1AAHAAAAB9AAA
6 rows selected.

除了上面排序的问题,关于检索结果的排序,还碰见另一个坑,如果是Oracle 9i,使用group by,会默认按照group by的字段顺序,进行order by排序,但如果迁移至Oracle 10g,及以上的版本,就会因为不带order by,返回的结果,出现乱序,可以参考《》。


因此,Oracle中,如果对检索结果,有排序的需求,就一定加上order by,明确指定排序的字段。

以上是关于Oracle读取数据的顺序问题的主要内容,如果未能解决你的问题,请参考以下文章

Oracle 等待事件:您如何解释 db 文件分散/顺序读取?

Oracle 数据库 - 使用UEStudio修改dmp文件版本号,解决imp命令恢复的数据库与dmp本地文件版本号不匹配导致的导入失败问题,“ORACLE error 12547”问题处理(代码片段

Oracle SQL:如何读取和增加字段

Oracle分页查询排序数据重复问题

Oracle11g09_Oracle的启动和关闭

sql Oracle代码片段