http://www.hzoes.com/ rowid與rownum不一樣

jing108發表於2012-05-05
,(rownum不能以任何表的名稱作為字首, 和*一起使用的時候, *前面必須加表名或者表的別名。)rowid前面可以加表名或者表別名,可以向其他普通列一樣使用。

  本文討論的是關於oracle從8i開始引進object的概念後的rowid,即擴充套件(extended)的rowid:

  1. rowid的介紹

  先對rowid有個感官認識:

  SQL> select ROWID from Bruce_test where rownum<2;

  ROWID

  ------------------ ----------

  AAABnlAAFAAAAAPAAA

  ROWID的格式如下:

  資料物件編號 檔案編號 塊編號 行編號

  OOOOOO FFF BBBBBB RRR

  我們可以看出,從上面的rowid可以得知:

  AAABnl 是資料物件編號

  AAF是相關檔案編號

  AAAAAP是塊編號

  AAA 是行編號

  怎麼依據這些編號得到具體的十進位制的編碼值呢,這是經常遇到的問題。這裡需要明白rowid的是基於64位編碼的18個字元顯示(資料物件編號(6) +檔案編號(3) +塊編號(6)+ 行編號(3)=18位),其中

  A-Z <==> 0 - 25 (26)

  a-z <==> 26 - 51 (26)

  0-9 <==> 52 - 61 (10)

  +/ <==> 62 - 63 (2)

  共64位,明白這個後,就可以計算出10進位制的編碼值,計算公式如下:

  d * (b ^ p)

  其中:b就是基數,這裡就是64,p就是從右到左,已0開始的位置數

  比如:上面的例子

  檔案號AAF,具體的計算應該是:

  5*(64^0)=5;

  0*(64^1)=0;

  0*(64^2)=0;

  檔案號就是0+0+5=5

  剛才提到的是rowid的顯示方式:基於64位編碼的18個字元顯示,其實rowid的儲存方式是:10 個位元組即80位儲存,其中資料物件編號需要32 位,相關檔案編號需要10 位,塊編號需要22,位行編號需要16 位,由此,我們可以得出:

  32bit的object number,每個資料庫最多有4G個物件

  10bit的file number,每個物件最多有1022個檔案(2個檔案預留)

  22bit的block number,每個檔案最多有4M個BLOCK

  16bit的row number,每個BLOCK最多有64K個ROWS

  2. rowid相關的有用的sql

  最簡單的基於rowid的顯示方式得到的響應的64位編碼對應值的sql:

  select rowid ,

  substr(rowid,1,6) "OBJECT",

  substr(rowid,7,3) "FILE",

  substr(rowid,10,6) "BLOCK",

  substr(rowid,16,3) "ROW"

  from TableName;

  OWID OBJECT FILE BLOCK ROW

  ------------------ ------------ ------ ------------ ------

  AAABc4AADAAAGLUAAA AAABc4 AAD AAAGLU AAA

  AAABc4AADAAAGLUAAB AAABc4 AAD AAAGLU AAB

  AAABc4AADAAAGLUAAC AAABc4 AAD AAAGLU AAC

  AAABc4AADAAAGLUAAD AAABc4 AAD AAAGLU AAD

  AAABc4AADAAAGLUAAE AAABc4 AAD AAAGLU AAE

  透過dbms_rowid這個包,可以直接的得到具體的rowid包含的資訊:

  select dbms_rowid.rowid_object(rowid) object_id, dbms_rowid.rowid_relative_fno(rowid) file_id,

  dbms_rowid.rowid_block_number(rowid) block_id ,dbms_rowid.rowid_row_number(rowid) num from bruce_t where rownum<5;

  OBJECT_ID FILE_ID BLOCK_ID NUM

  ---------- ---------- ---------- ----------

  5944 3 25300 0

  5944 3 25300 1

  5944 3 25300 2

  5944 3 25300 3

  一些使用ROWID的函式

  ROWIDTOCHAR(rowid) :將ROWID轉換成STRING

  CHARTOROWID('rowid_string') :將STRING轉換成ROWID

  另外,就是自己寫的一些函式:(下面的函式是網友eygle提供)

  create or replace function get_rowid

  (l_rowid in varchar2)

  return varchar2

  is

  ls_my_rowid varchar2(200);

  rowid_type number;

  object_number number;

  relative_fno number;

  block_number number;

  row_number number;

  begin

  dbms_rowid.rowid_info(l_rowid,rowid_type,object_number,relative_fno, block_number, row_number);

  ls_my_rowid := 'Object# is :'||to_char(object_number)||chr(10)||

  'Relative_fno is :'||to_char(relative_fno)||chr(10)||

  'Block number is :'||to_char(block_number)||chr(10)||

  'Row number is :'||to_char(row_number);

  return ls_my_rowid ;

  end;

  /

  應用上面的函式如下:

  SQL> select get_rowid(rowid), name from bruce_t;

  GET_ROWID(ROWID) NAME

  -------------------------------------------------------------------------------- --------------------------------

  Object# is :5944 BruceLau

  Relative_fno is :3

  Block number is :25300

  Row number is :0

  Object# is :5944 MabelTang

  Relative_fno is :3

  Block number is :25300

  Row number is :1

  ROWID:

  ROWID為該錶行的唯一標識,是一個偽列,可以用在SELECT中,但不可以用INSERT,, UPDATE來修改該值。

  注意:ROWID的表指,普通表,cluster table, partition table, subpartition table, index, index partitions and subpartitions(注意:不包含index-organized tables).

  每個表Oracle都存在一個偽列ROWID,這個偽列可以用SELECT檢視,但是不可以用INSERT, UPDATE來修改。你也不可以用DELETE來刪除

  ROWID列,,Oracle使用ROWID列來建立內部索引。你可以引用ROWID的值,但ROWID並不存放在資料庫中,你可以建立一個表包含ROWID資料型別,

  但Oracle不保證該值是合法的rowids。使用者必須確保該rowid值是真實合法的。

  UROWID:

  UROWID(可以稱為通用ROWID,邏輯ROWID): 表的行地址,表指的是index-organized tables。IOT中物理rowid是可能變化的,另外Oracle要依靠rowid來建立表的索引,所以對IOT表來物理rowid就不行了。Oracle以表的主鍵為基礎引入UROWID,在物理rowid基礎上建立了第二個索引。每一個邏輯rowid使用一個第二索引和一個物理推測(IOT中標識塊的行)。

  UROWID支援邏輯和物理的rowids,列UROWID型別可以儲存各種rowids, 從8.1以後的Oracle才有UROWID型別,它也可以用來儲存以前的ROWID型別資料資訊。

  更新IOT的主鍵可能導致ROWID改變,該行的UROWID也會改變。

  Oracle使用rowid資料型別儲存行地址,rowid可以分成兩種,分別適於不同的對像

  Physical rowids:儲存ordinary table,clustered table,table partition and subpartition,indexe,index partition and subpartition

  Logical rowids :儲存IOT的行地址

  另一種rowid型別叫universal rowed(UROWID),支援上述physical rowid和logical rowed,並且支援非oracle table,即支援所有型別的rowid,但COMPATIBLE必須在8.1或以上.

  1.1 ROWID偽列

  每個表在oracle內部都有一個ROWID偽列,,它在所有sql中無法顯示,不佔儲存空間;它用於從表中查詢行的地址或者在where中進行參照,一個例子如下:

  SELECT ROWID, last_name FROM employees;

  Oracle內部使用保留在ROWID偽列中的值構建索引結構

  再次強調一次,rowid偽列不儲存在資料庫中,它不是資料庫資料,這是從database及table的邏輯結構來說的,事實上,在物理結構上,每行由一個或多個row pieces組成,每個row piece的頭部包含了這個piece的address,即rowid.從這個意義上來說,rowid還是佔了磁碟空間的.

  我們在建立表時,可以為列指定為rowid資料型別,但oracle並不保證列中的資料是合法的rowid值,必須由應用程式來保證,另外,型別為rowid的列需要6 bytes儲存資料

  1.2, physical rowids

  只在行存在,它的實體地址rowid就不會變化,除非export/import,根據rowid可以直接定位到block去fetch資料,所以physical兼具有高穩定(stability)和高效能(performance)的特點.

  這裡要注意一點,對於clustered table來說,根據它的儲存特點,在同一個block中的不同table的行可能具有同一個rowid;而nonclustered table,每一行或初始行片(initial row piece)都有唯一的rowid

  要注意rowid的地址固定的特點,在一個block的某一行被delete並commit後,它佔據的address可以被其它事務新insert的行重用.

  Physical rowid可以是下面任一一種格式:

  1) Extended rowid

  使用表空間相關的資料塊地址,8i及以上使用這種格式

  2) Restricted rowid

  使用資料庫範圍的資料址地址,oracle 7或更早前的版本使用

  1.2.1extened rowid

  擴充套件行地址是64編碼的實體地址,編碼字元是A-Z, a-z, 0-9, +,and/.

  由4部分組成OOOOOOFFFBBBBBBRRR (obj#file#block#row#)

  OOOOOO -–data object number

  FFF –-表空間相對的資料檔案號

  BBBBBB –-塊號

  RRR ---行號

  注意不是16進製表示

  SQL> select rowid,name from obj$ where rownum<=10;

  ROWID NAME

  ------------------ ------------------------------

  AAAAASAABAAAAB6ABc ACCESS$

  AAAAASAABAAAC1QAAK AGGXMLIMP

  AAAAASAABAAAC1QAAL AGGXQIMP

  AAAAASAABAAAGiRAAI ALERT_QT

  AAAAASAABAAAGiRAAh ALERT_QUE

  AAAAASAABAAAGujAAo ALERT_QUE$1

  AAAAASAABAAAGujAAp ALERT_QUE$1

  AAAAASAABAAAGiRAAf ALERT_QUE_N

  AAAAASAABAAAGiRAAe ALERT_QUE_R

  AAAAASAABAAAGiRAAG ALERT_TYPE

  我們可以使用dbms_rowid從extened rowid中抽取各部分資訊,或者將extened rowid轉換成restricted rowed,詳細的資訊參見sys.dbms_rowid的規範

  #根據rowid抽塊對像編號

  SQL> select dbms_rowid.rowid_object('AAAAASAABAAAGiRAAG') obj# from dual;

  OBJ#

  ----------

  18

  #根據rowid抽取表空間相對檔案號

  SQL> select dbms_rowid.rowid_relative_fno('AAAAASAABAAAGiRAAG') rfile# from dual;

  RFILE#

  ----------

  1

  #根據rowid抽取塊號

  SQL> select dbms_rowid.ROWID_BLOCK_NUMBER('AAAAASAABAAAGiRAAG') block# from dual;

  BLOCK#

  ----------

  26769

  #根據rowid抽取行號

  SQL> select dbms_rowid.rowid_row_number('AAAAASAABAAAGiRAAG') row# from dual;

  ROW#

  ----------

  6

  #將extended rowid轉換成為restricted rowid

  SQL> select dbms_rowid.rowid_to_restricted('AAAAASAABAAAGiRAAG',0) restricted_rowid from dual;

  RESTRICTED_ROWID

  ------------------

  00006891.0006.0001

  1.2.2restricted rowid

  限制地址行號與擴充套件地址行號編碼方式不一樣,它在內部使用二進位制方式表示,當用select查詢時,會轉換成varchar2/16進位制的混合形式,它的組織方式如下:

  BBBBBBBB.RRRR.FFFF (block#.row#.file#)

  注意,這裡的檔案號是絕對檔案號,而extended rowid中是相對檔案號(相對錶空間)

  Restricted rowid中不再有object number,因為從絕對檔案號可以唯一確定資料塊

  樣例可以參考前面的00006891.0006.0001

  另外請注意,塊中的行號是從0開始

  除了用dbms_rowid來抽取rowid的不同部分外,也可以用substr

  #extended rowid

  SQL> SELECT ROWID,

  2007-02-01 15:19:28

  2 SUBSTR(ROWID,1,6) "OBJECT",

  3 SUBSTR(ROWID,7,3) "FIL",

  4 SUBSTR(ROWID,10,6) "BLOCK",

  5 SUBSTR(ROWID,16,3) "ROW"

  6 from obj$ where rownum<=5;

  ROWID OBJECT FIL BLOCK ROW

  ------------------ ------------ ------ ------------ ------

  AAAAASAABAAAAB6AAa AAAAAS AAB AAAAB6 AAa

  AAAAASAABAAAAB6AAu AAAAAS AAB AAAAB6 AAu

  AAAAASAABAAAAB6AAF AAAAAS AAB AAAAB6 AAF

  AAAAASAABAAAAB6AAv AAAAAS AAB AAAAB6 AAv

  AAAAASAABAAAAB6AAZ AAAAAS AAB AAAAB6 AAZ

  #restricted rowid

  SQL> SELECT ROWID,

  2 SUBSTR(ROWID,15,4) "FILE",

  3 SUBSTR(ROWID,1,8) "BLOCK",

  4 SUBSTR(ROWID,10,4) "ROW"

  5 from obj$ where rownum<=5;

  ROWID FILE BLOCK ROW

  ------------------ -------- ---------------- --------

  AAAAASAABAAAAB6AAa 6AAa AAAAASAA AAAA

  AAAAASAABAAAAB6AAu 6AAu AAAAASAA AAAA

  AAAAASAABAAAAB6AAF 6AAF AAAAASAA AAAA

  AAAAASAABAAAAB6AAv 6AAv AAAAASAA AAAA

  AAAAASAABAAAAB6AAZ 6AAZ AAAAASAA AAAA

  請注意extented rowid與restricted rowid的編碼方式不一樣,大家不能拿兩種不同編碼方式的元件作比較,比如AAAAASAABAAAAB6AAa 這行的File#在兩種方式下是有不同的值,表示不同的意義,沒有可比性.

  下面的語句可以檢視錶的資料分佈在幾個檔案中

  SQL> SELECT COUNT(DISTINCT(SUBSTR(ROWID,7,3))) "FILES" FROM BOSSSTATSDATA;

  FILES

  ----------

  17

  #下面驗證bossstatsdata的資料確實分佈在17個檔案中

  SQL> select count(file_name) from dba_data_files where TABLESPACE_NAME= (select TABLESPACE_NAME from user_tables where table_name='BOSSSTATSDATA');

  COUNT(FILE_NAME)

  ----------------

  17

  總結Rowid的使用場景

  1) 構建索引結構, 每個key都有一個rowid指向相應的錶行

  2) rowid是訪問錶行的最快的方法

  3) rowid可用於觀察表資料是怎樣組織的

  4) rowid是錶行的唯一識別符號

  在任何DML中使用rowid時,應該注意確保相關的行不會改變實體地址(不會被export/import,delete)

  1.3 logical rowids

  用於表達IOT行地址的Logical rowid儲存在索引的葉子節點中,會隨著索引entry的insert在塊內或塊間移動,所以,它不是基於實體地址而是基於primary key的識別符號,所以取名叫logcial rowid

  Oracle使用logical rowids來構建IOT的secondary indexes

  由於在實際的應用中很少會使用到IOT這種對像,關於logical rowid更詳細的描述可以參見<>中相關章節

  Part IV Oracle Database Application Development

  26 Native Datatypes

  Overview of ROWID and UROWID Datatypes

  1.4 非oracle table中的rowid

  在非oracle系統中,不同的系統有不同的rowid格式,並且,不能使用前述標準的rowid到varchar2/16進位制的轉換方法, 所以,在這種情況下,應用程式可以使用rowid資料型別,不過要使用非標準的轉換方法 (最長256bytes的16進位制)

  非oracle 系統中的rowid也能儲存在UROWID資料型別中

  作者“jukyoc”

  如何用rowid分頁

  --普通寫法(顯示1-50頁)

  select id, nick, auction_url, gmt_create

  from (select id, nick, auction_url, gmt_create, rownum linenum

  from (select id, nick, auction_url, gmt_create

  from activity_pepsi_dream t

  where status = 0

  order by t.GMT_CREATE desc) r

  WHERE rownum <= 50)

  WHERE linenum >= 1

  --rowid寫法

  select

  t2.id,

  t2.nick,

  t2.auction_url,

  t2.gmt_create

  from (select rid

  from (select r.rid, rownum linenum

  from (select

  rowid rid

  from activity_pepsi_dream t

  where status = 0

  order by t.GMT_CREATE desc) r

  WHERE rownum <= 50)

  WHERE linenum >=1) t1,

  activity_pepsi_dream t2

  where t1.rid = t2.rowid

  效能比較:

  ---查詢第一頁普通寫法要比rowid寫法要快

  ---隨著翻頁次數的增多,普通寫法需要回表的記錄越來越多,效能下降很快。比如你要看200-250條的記錄,這時候普通寫法需要回表250條記錄,而rowid寫法只要會標200-250區間中的這50條記錄

  rowid 寫法最佳化的情況:

  --第一層必須都在索引中掃描,不回表

  --第二三層找到滿足條件的rowid

  --最後根據rowid去回表找到記錄

  總結:關鍵就是oracle 的 rowid 屬性可以最快速度的尋找到該行的資料,並且固定,不需要回表操作。

  。
<!-- 正文結束 --&gt

相關文章