Oracle僞列的使用總結
僞列是指在創建表時不用我們自己創建,而是由Oracle自動創建的列。Oracle可以使用的僞列分爲以下幾種:
(1)在普通查詢中使用的僞列:ora_rowscn,rowid,rownum;
(2)在Flashback Version Query查詢中使用的僞列:versions_starttime,versions_endtime,versions_startscn,versions_endscn,versions_operation,versions_xid;
(3)在遞歸查詢中使用的僞列:connect_by_isleaf,level,connect_by_iscycle。
一、普通查詢中使用的僞列
1、ora_rowscn僞列
默認情況下,ora_rowscn的查詢結果是從數據文件塊頭獲取的,查詢出的是block的最近事務的scn,而不是精確到row的scn。在創建表時,可以指定rowdependencies來使ora_rowscn真正記錄行一級的scn。
舉例:
(1)創建一張表stu,指定rowdependencies參數
SQL> create table stu(id char(11) primary key,name varchar2(20),age number(2)) rowdependencies;
Table created.
(2)插入三條記錄,查詢ora_rowscn
SQL> select ora_rowscn,stu.* from stu;
ORA_ROWSCN ID NAME AGE
---------- ----------- -------------------- ----------
11285682 20180224001 張雲 20
11285682 20180224002 李輝 21
11285682 20180224012 張華 19
(3)對第一條記錄進行修改並提交,然後重新查詢ora_rowscn
SQL> update stu set age=18 where id='20180224001';
1 row updated.
Elapsed: 00:00:00.00
SQL> commit;
Commit complete.
Elapsed: 00:00:00.02
SQL> select ora_rowscn,stu.* from stu;
ORA_ROWSCN ID NAME AGE
---------- ----------- -------------------- ----------
11285703 20180224001 張雲 18
11285682 20180224002 李輝 21
11285682 20180224012 張華 19
(4)利用SCN_TO_TIMESTAMP函數可以查詢scn號對應的具體時間
SQL> select ora_rowscn,SCN_TO_TIMESTAMP(ora_rowscn),stu.* from stu;
ORA_ROWSCN SCN_TO_TIMESTAMP(ORA_ROWSCN) ID NAME AGE
---------- --------------------------------------------------------------------------- ---
11285703 26-FEB-20 11.27.19.000000000 AM 20180224001 張雲 18
11285682 26-FEB-20 11.26.25.000000000 AM 20180224002 李輝 21
11285682 26-FEB-20 11.26.25.000000000 AM 20180224012 張華 19
Elapsed: 00:00:00.04
2、ROWID僞列
rowid與磁盤驅動器中的特定位置相關,rowid就是表中的行存在於文件系統中的物理位置,oracle數據庫每一行都有一個rowid值。一旦一行數據插入數據庫,則rowid在該行的生命週期內是唯一的,即即使該行產生行遷移,行的rowid也不會改變。索引結構中也包含rowid,通過索引能快速的定位表中的記錄。
Rowid是基於64位編碼的18位字符,格式如下:
data_object_id file_id block_number row_number
OOOOOO FFF BBBBBB RRR
查看錶中的rowid僞列數據:
SQL> select rowid,e.empno,e.ename,e.sal,e.comm from emp e;
ROWID EMPNO ENAME SAL COMM
------------------ ---------- ---------- ---------- ----------
AAASE7AAEAAAACWAAA 7698 BLAKE 2850
AAASE7AAEAAAACWAAM 7499 ALLEN 1600 300
AAASE7AAEAAAACWAAN 7788 SCOTT 3000 500
AAASE7AAEAAAACWAAP 7876 ADAMS 1100
AAASE7AAEAAAACWAAQ 7654 MARTIN 1250 1400
AAASE7AAEAAAACWAAR 7900 JAMES 950
AAASE7AAEAAAACWAAV 7566 JONES 2975
AAASE7AAEAAAACWAAW 7902 FORD 3000
AAASE7AAEAAAACWAAX 7369 SMITH 800
AAASE7AAEAAAACWAAY 7521 WARD 1250 500
AAASE7AAEAAAACWAAZ 7844 TURNER 1500 0
AAASE7AAEAAAACWAAa 8002 TOM 4000 500
AAASE7AAEAAAACWAAb 7934 MILLER 1300 2
AAASE7AAEAAAACWAAc 7782 CLARK 2450 2
AAASE7AAEAAAACWAAd 7839 KING 5000 2
15 rows selected.
3、ROWNUM僞列
在查詢結果中,每返回一條記錄,rownum僞列就返回一個數字,代表查詢返回的行的編號。rownum返回的是查詢過程中返回記錄的順序,並不是查詢結果的序列號,因此對於某一條記錄來說rownum的值是不確定的,每次查詢都有可能對應不同的取值。
如果使用rownum僞列構造查詢條件,只能使用(<、<=、!=),不能使用(>,>=,=,between…and),因爲rownum是針對查詢的結果集加的一個僞列,即先查到結果集之後再加上去的一個列,rownum 是對符合條件結果的序列號,如果使用(>,>=,=,between…and)構造查詢條件,從緩衝區或數據文件中得到的第一條記錄的rownum爲1,則被刪除,接着取下條,可是它的rownum還是1,又被刪除,依次類推,便查詢不到任何數據。如果想使用(>,>=,=,between…and)構造查詢條件,可以使用子查詢解決。
舉例:
SQL> select * from (select rownum rn,emp.* from emp) e where e.rn between 3 and 7;
RN EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- ---------- --------- ---------- ------------------- ----------
3 7788 SCOTT ANALYST 7566 1987-04-19 00:00:00 3000 500 20
4 7876 ADAMS CLERK 7788 1987-05-23 00:00:00 1100 20
5 7654 MARTIN SALESMAN 7698 1981-09-28 00:00:00 1250 1400 30
6 7900 JAMES CLERK 7698 1981-12-03 00:00:00 950 30
7 7566 JONES MANAGER 7839 1981-04-02 00:00:00 2975 20
Elapsed: 00:00:00.00
二、Flashback Version Query查詢中的僞列
Flashback Version Query查詢稱爲Oracle的閃回版本查詢,閃回版本查詢用來獲取在給定的時間區間中,指定行的不同版本。當COMMIT語句被執行時,一個新的行版本被創建。閃回版本查詢使用VERSIONS BETWEEN子句,閃回版本查詢中可以使用versions_starttime,versions_endtime,versions_startscn,versions_endscn,versions_operation,versions_xid僞列來獲取相關信息。格式如下:
VERSIONS {BETWEEN {SCN | TIMESTAMP} start AND end}
說明:
(1)start和end是代表開始和結束的表達式,代表被查詢的時間區間。
(2)閃回版本查詢返回一個表,包含行在指定的時間區間中的所有版本。在表中的每行都包含關於行版本的元數據僞列。
例如:對emp表的7788號員工的comm字段進行多次修改並且提交。
SQL> select systimestamp from dual;
SYSTIMESTAMP
---------------------------------------------------------------------------
25-FEB-20 08.52.50.309623 PM +08:00
Elapsed: 00:00:00.01
SQL> update emp set comm=100 where empno=7788;
1 row updated.
Elapsed: 00:00:00.01
SQL> commit;
Commit complete.
Elapsed: 00:00:00.01
SQL> update emp set comm=300 where empno=7788;
1 row updated.
Elapsed: 00:00:00.00
SQL> commit;
Commit complete.
Elapsed: 00:00:00.01
SQL> update emp set comm=500 where empno=7788;
1 row updated.
Elapsed: 00:00:00.01
SQL> commit;
Commit complete.
查詢emp表的修改信息(顯示修改時間):
SQL>
SELECT versions_starttime, versions_endtime, versions_xid, versions_operation,
emp.empno,emp.ename,emp.comm
FROM emp
versions between timestamp
to_timestamp('2020-2-25 20.52.50','yyyy-mm-dd hh24:mi:ss')
AND systimestamp
WHERE empno=7788;
VERSIONS_STARTTIME VERSIONS_ENDTIME VERSIONS_XID V EMPNO ENAME COMM
--------------------------------------------------------------------------- -
25-FEB-20 08.53.26 PM 040021005E0A0000 U 7788 SCOTT 500
25-FEB-20 08.53.17 PM 25-FEB-20 08.53.26 PM 08001B00890F0000 U 7788 SCOTT 300
25-FEB-20 08.53.11 PM 25-FEB-20 08.53.17 PM 070015004C0A0000 U 7788 SCOTT 100
25-FEB-20 08.53.11 PM 7788 SCOTT
查詢emp表的修改信息(顯示修改的SCN號):
SQL>
SELECT versions_startscn, versions_endscn, versions_xid, versions_operation, emp.empno,emp.ename,emp.comm
FROM emp
versions between timestamp to_timestamp('2020-2-25 20.52.50','yyyy-mm-dd hh24:mi:ss')
AND systimestamp
5 WHERE empno=7788;
VERSIONS_STARTSCN VERSIONS_ENDSCN VERSIONS_XID V EMPNO ENAME COMM
----------------- --------------- ---------------- - ---------- ---------- ----------
11242056 040021005E0A0000 U 7788 SCOTT 500
11242052 11242056 08001B00890F0000 U 7788 SCOTT 300
11242049 11242052 070015004C0A0000 U 7788 SCOTT 100
11242049 7788 SCOTT
查詢各個時間點的數據:
SQL> select * from emp as of scn 11242049 where empno=7788;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- ------------------- ---------- ---------- -------
7788 SCOTT ANALYST 7566 1987-04-19 00:00:00 3000 100 20
Elapsed: 00:00:00.01
SQL> select * from emp as of scn 11242052 where empno=7788;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- ------------------- ---------- ---------- ------
7788 SCOTT ANALYST 7566 1987-04-19 00:00:00 3000 300 20
Elapsed: 00:00:00.00
SQL> select * from emp as of scn 11242056 where empno=7788;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- ------------------- ---------- ---------- ------
7788 SCOTT ANALYST 7566 1987-04-19 00:00:00 3000 500 20
三、遞歸查詢中使用的僞列
在select語句中可以使用start with…connect by prior子句實現遞歸查詢,其基本語法如下:
SELECT ...
FROM ....
START WITH cond1
CONNECT BY cond2
WHERE cond3;
說明:
(1)START WITH:表示從哪個節點開始查找,也就是通過cond1查詢到的數據,作爲後續查詢的起始節點(參數),可以是一個或多個根節點;如果省略Start With,就把整個表中的數據從頭到尾遍歷一次,每一個數據做一次根,然後遍歷樹中的其他節點信息;
(2)CONNECT BY子句說明每行數據將是按照層次順序檢索,cond2是連接條件,一般包含PRIOR,如果PRIOR在子節點(ID)之前,表示向下查找,如果PRIOR在父節點(PID)之前,表示向上查找;如果不帶PRIOR,則只顯示當前記錄;
(3)WHERE:這裏的條件判斷用於在最後查詢出結果列表之後再進行條件篩選;
(4)可以使用僞列connect_by_isleaf判斷一個節點是否是葉子節點;
(5)可以使用僞列level查詢當前節點所處層級,這裏的層級指的是從start with查詢到的節點開始往下算起。
舉例:
1、數據準備
創建一張表area,表結構如下:
10:59:43 SQL> desc area;
Name Null? Type
-----------------------------------------------------------------------------------
ID NOT NULL NUMBER(38)
DSC VARCHAR2(20)
PID NUMBER(38)
該表以樹狀結構保存地區,表中的PID(parent ID)字段存儲的某個地區的上級ID,如果是樹的根節點,則PID爲0。
插入記錄,結果如下:
11:05:34 SQL> select * from area;
ID DSC PID
---------- -------------------- ----------
1 中國 0
101 河南省 1
102 湖北省 1
103 河北省 1
10101 鄭州市 101
10102 新鄉市 101
10103 開封市 101
10104 駐馬店市 101
10105 南陽市 101
10106 信陽市 101
10107 濮陽市 101
10108 安陽市 101
1010201 紅旗市 10102
1010202 衛濱區 10102
1010203 牧野市 10102
1010204 原陽縣 10102
1010205 獲嘉縣 10102
1010206 衛輝市 10102
1010401 驛城區 10104
1010402 上蔡縣 10104
1010403 新蔡縣 10104
1010404 正陽縣 10104
1010405 西平縣 10104
1010406 汝南縣 10104
10201 武漢市 102
10202 黃岡市 102
10203 黃石市 102
10204 孝感市 102
1020101 武昌區 10201
1020102 漢口區 10201
1020103 漢陽區 10201
31 rows selected.
2、查找一個節點所有的下層節點
SQL>
SELECT area.*
FROM area
START WITH id=101
3 4 CONNECT BY prior id=pid;
ID DSC PID
---------- -------------------- ----------
101 河南省 1
10101 鄭州市 101
10102 新鄉市 101
1010201 紅旗市 10102
1010202 衛濱區 10102
1010203 牧野市 10102
1010204 原陽縣 10102
1010205 獲嘉縣 10102
1010206 衛輝市 10102
10103 開封市 101
10104 駐馬店市 101
1010401 驛城區 10104
1010402 上蔡縣 10104
1010403 新蔡縣 10104
1010404 正陽縣 10104
1010405 西平縣 10104
1010406 汝南縣 10104
10105 南陽市 101
10106 信陽市 101
10107 濮陽市 101
10108 安陽市 101
21 rows selected.
Elapsed: 00:00:00.03
3、查找一個節點所有的上層節點
SQL>
SELECT area.*
FROM area
START WITH id=10102
4 CONNECT BY id=prior pid;
ID DSC PID
---------- -------------------- ----------
10102 新鄉市 101
101 河南省 1
1 中國 0
Elapsed: 00:00:00.00
4、判斷節點是否是葉子節點
SQL>
SELECT area.*,
decode(connect_by_isleaf,0,'非葉子節點',1,'葉子節點')
as isleaf_node
FROM area
START WITH id=101
6 CONNECT BY prior id=pid;
ID DSC PID ISLEAF_NODE
---------- -------------------- ---------- ---------------
101 河南省 1 非葉子節點
10101 鄭州市 101 葉子節點
10102 新鄉市 101 非葉子節點
1010201 紅旗市 10102 葉子節點
1010202 衛濱區 10102 葉子節點
1010203 牧野市 10102 葉子節點
1010204 原陽縣 10102 葉子節點
1010205 獲嘉縣 10102 葉子節點
1010206 衛輝市 10102 葉子節點
10103 開封市 101 葉子節點
10104 駐馬店市 101 非葉子節點
1010401 驛城區 10104 葉子節點
1010402 上蔡縣 10104 葉子節點
1010403 新蔡縣 10104 葉子節點
1010404 正陽縣 10104 葉子節點
1010405 西平縣 10104 葉子節點
1010406 汝南縣 10104 葉子節點
10105 南陽市 101 葉子節點
10106 信陽市 101 葉子節點
10107 濮陽市 101 葉子節點
10108 安陽市 101 葉子節點
21 rows selected.
5、查詢葉子節點
SQL>
SELECT area.*
FROM area
WHERE connect_by_isleaf=1
START WITH id=101
5 CONNECT BY prior id=pid;
ID DSC PID
---------- -------------------- ----------
10101 鄭州市 101
1010201 紅旗市 10102
1010202 衛濱區 10102
1010203 牧野市 10102
1010204 原陽縣 10102
1010205 獲嘉縣 10102
1010206 衛輝市 10102
10103 開封市 101
1010401 驛城區 10104
1010402 上蔡縣 10104
1010403 新蔡縣 10104
1010404 正陽縣 10104
1010405 西平縣 10104
1010406 汝南縣 10104
10105 南陽市 101
10106 信陽市 101
10107 濮陽市 101
10108 安陽市 101
18 rows selected.
6、查詢非葉子節點
SQL>
SELECT area.*
FROM area
WHERE connect_by_isleaf=0
START WITH id=101
5 CONNECT BY prior id=pid;
ID DSC PID
---------- -------------------- ----------
101 河南省 1
10102 新鄉市 101
10104 駐馬店市 101
Elapsed: 00:00:00.00
7、判斷每個節點的層級
SQL>
SELECT area.*,level as level_n
FROM area
START WITH id=101
4 CONNECT BY prior id=pid;
ID DSC PID LEVEL_N
---------- -------------------- ---------- ----------
101 河南省 1 1
10101 鄭州市 101 2
10102 新鄉市 101 2
1010201 紅旗市 10102 3
1010202 衛濱區 10102 3
1010203 牧野市 10102 3
1010204 原陽縣 10102 3
1010205 獲嘉縣 10102 3
1010206 衛輝市 10102 3
10103 開封市 101 2
10104 駐馬店市 101 2
1010401 驛城區 10104 3
1010402 上蔡縣 10104 3
1010403 新蔡縣 10104 3
1010404 正陽縣 10104 3
1010405 西平縣 10104 3
1010406 汝南縣 10104 3
10105 南陽市 101 2
10106 信陽市 101 2
10107 濮陽市 101 2
10108 安陽市 101 2
21 rows selected.
從下向上查詢的結果:
SQL>
SELECT area.*,level as level_n
FROM area
START WITH id=101
4 CONNECT BY id=prior pid;
ID DSC PID LEVEL_N
---------- -------------------- ---------- ----------
101 河南省 1 1
1 中國 0 2
8、統計節點的層數
SQL>
SELECT count(distinct level)
FROM area
START WITH id=1
4 CONNECT BY prior id=pid;
COUNT(DISTINCTLEVEL)
--------------------
4
Elapsed: 00:00:00.01
9、查詢每一層節點的個數
SQL>
WITH tmp AS
(SELECT area.*,level as lev
FROM area
START WITH id=1
CONNECT BY prior id=pid)
SELECT 'Level-'||lev as level_count,
count(1) as node_count
FROM tmp
9 GROUP BY lev;
LEVEL_COUNT NODE_COUNT
---------------------------------------------- ----------
Level-1 1
Level-2 3
Level-4 15
Level-3 12
10、查詢某一個節點的子節點
SQL>
SELECT area.*,level as level_n
FROM area
WHERE level=2
START WITH id=101
5 CONNECT BY prior id=pid;
ID DSC PID LEVEL_N
---------- -------------------- ---------- ----------
10101 鄭州市 101 2
10102 新鄉市 101 2
10103 開封市 101 2
10104 駐馬店市 101 2
10105 南陽市 101 2
10106 信陽市 101 2
10107 濮陽市 101 2
10108 安陽市 101 2
8 rows selected.
Elapsed: 00:00:00.00
11、查詢某一個節點的父節點
SQL>
SELECT area.*,level as level_n
FROM area
WHERE level=2
START WITH id=101
5 CONNECT BY id=prior pid;
ID DSC PID LEVEL_N
---------- -------------------- ---------- ----------
1 中國 0 2
Elapsed: 00:00:00.00
12、格式化節點的顯示
SQL>
SELECT LPAD(' ',(level-1)*4)||dsc as area
FROM area
START WITH id=1
4 CONNECT BY prior id=pid;
AREA
----------------------------------
中國
河南省
鄭州市
新鄉市
紅旗市
衛濱區
牧野市
原陽縣
獲嘉縣
衛輝市
開封市
駐馬店市
驛城區
上蔡縣
新蔡縣
正陽縣
西平縣
汝南縣
南陽市
信陽市
濮陽市
安陽市
湖北省
武漢市
武昌區
漢口區
漢陽區
黃岡市
黃石市
孝感市
河北省
31 rows selected.
Elapsed: 00:00:00.00