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