天天看點

oracle 中的僞列和表

1、oracle的僞列

oracle系統為了實作完整的關系資料庫功能,系統專門提供了一組稱為僞列(Pseudocolumn)的資料庫列,這些列不是在建立對象時由我們完成的,而是在我們建立時由Oracle完成的。Oracle目前常見的僞列: 

currval 和 nextval 使用序列号的保留字 

        CURRVAL:傳回目前sequence值

        NEXTVAL:增加sequence并傳回下一個值

 一般用法:

  sequence.CURRVAL

  sequence.NEXTVAL

level 查詢資料所對應的級 

     分級查詢是oracle提供的遞歸查詢文法,在這裡不做展開。隻有在分級查詢下,才可以使用以下僞列:

       CONNECT_BY_ISCYCLE Pseudocolumn

        CONNECT_BY_ISLEAF Pseudocolumn

        LEVEL Pseudocolumn

Version Query Pseudocolumns

Version Query僞列隻有在Flashback Version Query時才有效,内容如下:

        VERSIONS_STARTSCN and VERSIONS_STARTTIME

        VERSIONS_ENDSCN and VERSIONS_ENDTIME

        VERSIONS_XID

        VERSIONS_OPERATION

l        COLUMN_VALUE Pseudocolumn

l        OBJECT_ID Pseudocolumn

l        OBJECT_VALUE Pseudocolumn

l        ORA_ROWSCN Pseudocolumn

l        XMLDATA Pseudocolumn

  rowid 記錄的唯一辨別 

  rownum 限制查詢結果集的數量 

在對普通表做查詢時,比較常用的僞列有:ORA_ROWSCN、ROWID、ROWNUM。

ORA_ROWSCN

雖然叫ORA_ROWSCN,不過預設情況下,查詢出的該值是從資料檔案塊頭擷取的,也就是說,查詢出的是block的最近事務的scn,而不是精确到row的scn。

在建立表時,可以指定ROWDEPENDENCIES來使ora_rowscn真正記錄行一級的scn。

看一下ROWDEPENDENCIES 的定義:Specify ROWDEPENDENCIES if you want to enable row-level dependency tracking(追蹤). This setting

is useful primarily to allow for parallel propagation in replication environments. It increases the size of each row by 6 bytes.

從這裡也可以看出一個SCN占用的空間(6 bytes)。SCN的最大值是0xffff.ffffffff,共48位。包括2bytes的高位位元組(SCN

wrap)和4bytes的低位位元組(SCN base)。

SQL>select ora_rowscn,username from t;

ORA_ROWSCN USERNAME

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

 86516279 SYSTEM

 86516279 SYS

 86516279 OUTLN

 86516279 LINCINQ

 86516279 TEST

 86516279 LINC

 86516279 SPLEXUC

 86516279 DIP

  86516279 TSMSYS

 86516279 WMSYS

 86516279 DBSNMP

11 rows selected.

需要查詢scn對于的具體時間,可以用函數SCN_TO_TIMESTAMP

SQL>alter session set nls_timestamp_format='yyyy-mm-dd hh24:mi:ss';

 Session altered.

SQL>select scn_to_timestamp(ora_rowscn),username from t;

 SCN_TO_TIMESTAMP(ORA_ROWSCN)  USERNAME

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

2011-06-01 13:13:31           SYSTEM

2011-06-01 13:13:31           SYS

2011-06-01 13:13:31           OUTLN

2011-06-01 13:13:31           LINCINQ

2011-06-01 13:13:31           TEST

2011-06-01 13:13:31           LINC

2011-06-01 13:13:31           SPLEXUC

2011-06-01 13:13:31           DIP

2011-06-01 13:13:31           TSMSYS

2011-06-01 13:13:31           WMSYS

2011-06-01 13:13:31           DBSNMP

ROWID

rowid是一種特殊類型的列,又叫做rowid僞列。rowid僞列可以向正常列一樣使用sql select語句通路。oracle資料庫每一行都有一個

rowid僞列。rowid表示特定行的特定位址。rowid 僞列可以用rowid資料類型來定義。 

rowid與磁盤驅動器中的特定位置相關。是以,rowid是擷取某個行最快速的方法。然面,一個行的rowid會随着資料庫的解除安裝和重新加載而發生變化。基于這一點考慮,我們不推薦在多個事務代碼中使用rowid僞列的值。 

    使用者無法使用sql語句來設定标準的rowid僞列的數值。 

    使用者可以定義rowid類型的列或者變量,但是oracle并不保證存在這些列或者變量中的數值就是有效的rowid 

   rowid就是表記錄存在于檔案系統中的實體位置,索引結構中包含rowid,是以通過索引能快速的定位表中的記錄. 

rowid的存在時是了能根據它找到表的這條記錄存在哪個具體的實體位置,我們需要知道它在哪個資料檔案,在哪個block,在哪一行 

這就是修改某列時需要提供rowid的原因。 

Rowid的概念:rowid是一個僞列,既然是僞列,那麼這個列就不是使用者定義,而是系統自己給加上的。對每個表都有一個rowid的僞列,但是表中并不實體存儲ROWID列的值。不過你可以像使用其它列那樣使用它,但是不能删除改列,也不能對該列的值進行修改、插入。一旦一行資料插入資料庫,則rowid在該行的生命周期内是唯一的,即即使該行産生行遷移,行的rowid也不會改變。

Rowid是基于64位編碼的18個字元,格式如下:

data_object_id     file_id      block_number  row_number

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

  OOOOOO            FFF          BBBBBB         RRR

SQL>select rowid,username from t;

ROWID             USERNAME

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

AAAE0LAAOAABQqMAAA SYSTEM

AAAE0LAAOAABQqMAAB SYS

AAAE0LAAOAABQqMAAC OUTLN

AAAE0LAAOAABQqMAAD LINCINQ

AAAE0LAAOAABQqMAAE TEST

AAAE0LAAOAABQqMAAF LINC

AAAE0LAAOAABQqMAAG SPLEXUC

AAAE0LAAOAABQqMAAH DIP

AAAE0LAAOAABQqMAAI TSMSYS

AAAE0LAAOAABQqMAAJ WMSYS

AAAE0LAAOAABQqMAAK DBSNMP

通過dbms_rowid包,我們可以獲得rowid對應的記錄詳細資訊。

SQL>select dbms_rowid.rowid_object('&1') data_object_id#,

        dbms_rowid.rowid_relative_fno('&1') rfile#,

        dbms_rowid.rowid_block_number('&1') block#,

        dbms_rowid.rowid_row_number('&1') row# from dual;

Enter value for 1: AAAE0LAAOAABQqMAAA

old  1: select dbms_rowid.rowid_object('&1') data_object_id#,

new  1: select dbms_rowid.rowid_object('AAAE0LAAOAABQqMAAA') data_object_id#,

old  2:       dbms_rowid.rowid_relative_fno('&1') rfile#,

new  2:       dbms_rowid.rowid_relative_fno('AAAE0LAAOAABQqMAAA') rfile#,

old  3:       dbms_rowid.rowid_block_number('&1') block#,

new  3:       dbms_rowid.rowid_block_number('AAAE0LAAOAABQqMAAA') block#,

old  4:       dbms_rowid.rowid_row_number('&1') row# from dual

new  4:       dbms_rowid.rowid_row_number('AAAE0LAAOAABQqMAAA') row# from dual

DATA_OBJECT_ID#    RFILE#    BLOCK#      ROW#

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

         19723        14    330380         0

ROWNUM

在對表的查詢中,每傳回一條記錄,rownum僞列就傳回一個數字,代表查詢傳回的行的編号。

SQL>select rownum,username from t;

   ROWNUM USERNAME

        1 SYSTEM

        2 SYS

        3 OUTLN

        4 LINCINQ

        5 TEST

        6 LINC

        7 SPLEXUC

        8 DIP

        9 TSMSYS

       10 WMSYS

       11 DBSNMP

從下面的例子可以看出,rownum傳回的是查詢過程中傳回記錄的順序,并不是查詢結果的序列号。

SQL>select rownum,username from t order by username;

SQL>select rownum,username from (select username from t order by username);

        1 DBSNMP

        2 DIP

        3 LINC

        5 OUTLN

        6 SPLEXUC

        7 SYS

        8 SYSTEM

        9 TEST

       10 TSMSYS

       11 WMSYS

對于 Oracle 的 rownum 問題,很多資料都說不支援>,>=,=,between...and,隻能用以上符号(<、<=、!=),并非說用>,>=,=,between..and 時會提示SQL文法錯誤,而是經常是查不出一條記錄來,還會出現似乎是莫名其妙的結果來,其實您隻要了解好了這個 rownum 僞列的意義就不應該感到驚奇,同樣是僞列,rownum 與 rowid 可有些不一樣,下面以例子說明

假設某個表 t1(c1) 有 20 條記錄 

如果用 select rownum,c1 from t1 where rownum < 10, 隻要是用小于号,查出來的結果很容易地與一般了解在概念上能達成一緻,應該不會有任何疑問的。 

可如果用 select rownum,c1 from t1 where rownum > 10 (如果寫下這樣的查詢語句,這時候在您的頭腦中應該是想得到表中後面10條記錄),你就會發現,顯示出來的結果要讓您失望了,也許您還會懷疑是不誰删了一些記錄,然後檢視記錄數,仍然是 20 條啊?那問題是出在哪呢? 

先好好了解 rownum 的意義吧。因為ROWNUM是對結果集加的一個僞列,即先查到結果集之後再加上去的一個列 (強調:先要有結果集)。簡單的說 rownum 是對符合條件結果的序列号。它總是從1開始排起的。是以你選出的結果不可能沒有1,而有其他大于1的值。是以您沒辦法期望得到下面的結果集: 

11 aaaaaaaa 

12 bbbbbbb 

13 ccccccc 

................. 

rownum >10 沒有記錄,因為第一條不滿足去掉的話,第二條的ROWNUM又成了1,是以永遠沒有滿足條件的記錄。或者可以這樣了解: 

ROWNUM是一個序列,是oracle資料庫從資料檔案或緩沖區中讀取資料的順序。它取得第一條記錄則rownum值為1,第二條為2,依次類推。如果你用>,>=,=,between...and這些條件,因為從緩沖區或資料檔案中得到的第一條記錄的rownum為1,則被删除,接着取下條,可是它的rownum還是1,又被删除,依次類推,便沒有了資料。 

有了以上從不同方面建立起來的對 rownum 的概念,那我們可以來認識使用 rownum 的幾種現像 

1. select rownum,c1 from t1 where rownum != 10 為何是傳回前9條資料呢?它與 select rownum,c1 from tablename where rownum < 10 傳回的結果集是一樣的呢? 

    因為是在查詢到結果集後,顯示完第 9 條記錄後,之後的記錄也都是 != 10,或者 >=10,是以隻顯示前面9條記錄。也可以這樣了解,rownum 為9後的記錄的 rownum為10,因條件為 !=10,是以去掉,其後記錄補上,rownum又是10,也去掉,如果下去也就隻會顯示前面9條記錄了 

2. 為什麼 rownum >1 時查不到一條記錄,而 rownum >0 或 rownum >=1 卻總顯示是以的記錄 

   因為 rownum 是在查詢到的結果集後加上去的,它總是從1開始 

3. 為什麼 between 1 and 10 或者 between 0 and 10 能查到結果,而用 between 2 and 10 卻得不到結果 

   原因同上一樣,因為 rownum 總是從 1 開始 

從上可以看出,任何時候想把 rownum = 1 這條記錄抛棄是不對的,它在結果集中是不可或缺的,少了rownum=1 就像空中樓閣一般不能存在,是以你的 rownum 條件要包含到 1 

但如果就是想要用 rownum > 10 這種條件的話話就要用嵌套語句,把 rownum 先生成,然後對他進行查詢。 

select * from (selet rownum as rn,t1.* from a where ...) where rn >10 

一般代碼中對結果集進行分頁就是這麼幹的。 

另外:rowid 與 rownum 雖都被稱為僞列,但它們的存在方式是不一樣的,rowid 可以說是實體存在的,表示記錄在表空間中的唯一位置ID,在DB中唯一。隻要記錄沒被搬動過,rowid是不變的。rowid 相對于表來說又像表中的一般列,是以以 rowid 為條件就不會有 rownum那些情況發生。 

2、oracle的僞表 

dual 

該表主要目的是為了保證在使用SELECT語句中的語句的完整性而提供的。 

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