昨天的文章《v$和v_$的一些玄机》,有朋友提出了一些异议,如下相同名称的两个对象v
文章提到了,
"(1) 假设xbisal,他的基表是xbisal,create view vbisal as select * from xbisal;(3) 创建视图v_bisal,他是基于vbisal视图创建的,create view v_bisal as select * from vbisal;(4)创建公共同义词v$bisal,他表示的是v_$bisal视图,create public synonym v$bisal for v_$bisal;" ;"
准确地说,以上操作的目的,通过v_视图将v视图和普通用户隔离,这种机制适合系统动态性能视图的场景,不是普通用户自定义的视图场景,为什么这么说?
针对系统动态性能视图,数据库启动时,Oracle动态创建了x$表,在此基础之上,创建gv$和v$视图,Oracle创建了gv_$和v_$视图,然后创建了gv$和v$的公共同义词,而真正的v$视图访问的限制是通过软件机制实现的,不是数据库权限控制的。因此,用户访问的v$对象,不是视图,而是指向v_$视图的同义词,而v_$视图才是基于真正的v$视图(基于x$创建的)创建的。这才能达到通过v_$视图将v$视图和普通用户隔离。
P.S.
gv是Global v,除了一些例外,每个v视图都有个对应的gv视图,他是为了满足RAC环境的需要,gv返回所有实例的信息,每个v视图是基于gv视图,只是增加了inst_id列作为where条件,限定在当前实例的信息,例如vsession基于gv
gv$session定义(未显示完整),
针对普通用户自定义的视图,我们稍微改造下最开始的SQL,
1. x$bisal表包含id和a字段。
1. vbisal视图是基于xbisal表,包含id和a字段。
2. v_bisal视图是基于vbisal视图,但是只含max(id)字段。
3. v$bisal的公共同义词是基于v_$bisal视图,只含max(id)字段。
SQL> create table x$bisal (id number, a varchar2(1));
Table created.
SQL> create view v$bisal as select * from x$bisal;
View created.
SQL> create view v_$bisal as select max(id) x from v$bisal;
View created.
SQL> create public synonym v$bisal for v_$bisal;
Synonym created.
SQL> select object_name, object_type from dba_objects where object_name in ('V$BISAL','X$BISAL','V_$BISAL');
OBJECT_NAME OBJECT_TYPE
--------------- -------------------
V$BISAL SYNONYM
V$BISAL VIEW
V_$BISAL VIEW
X$BISAL TABLE
vbisal这个名称,既是视图的名称,又是公共同义词的名称。问题来了,执行检索vbisal,访问的是视图还是公共同义词?
公共同义词v$bisal指向的是含一个字段的视图v_$bisal,视图v$bisal是含两个字段的。我们执行的“desc v$bisal”,返回两个字段,所以可确认他访问的是视图v$bisal,说明在进行数据访问的时候,先访问的是视图,其优先级要高于公共同义词,
SQL> desc v$bisal
Name Null? Type
------------- -------- ----------------------------
ID NUMBER
A VARCHAR2(1)
如下是我们创建的顺序,
xbisal(表) -> vbisal(视图) -> v
但实际执行v$bisal得到的是v$bisal(视图),不是v$bisal(同义词),因此,针对普通用户自定义的视图,不能通过v_$视图将v$视图和普通用户隔离。
P.S.
eygle的书中介绍了Oracle对于对象名的解析顺序,
(1) Oracle首先查看在发出命令的用户模式中是否存在表或视图。
(2) 如果表或视图不存在,Oracle会看私有同义词是否存在。
(3) 如果私有同义词存在,将使用这个同义词所引用的对象。
(4) 如果私有同义词不存在,看同名的公共同义词是否存在。
(5) 如果公共同义词存在,将使用这个同义词所引用的对象。
(6) 如果公共同义词不存在,返回信息"ORA-00942 table or view does not exit"。
朋友提出第二个问题,为什么能创建出两个相同名称的对象v$bisal?
我们知道,同义词分为public和private,public同义词属于PUBLIC组,每个用户都可以访问,private同义词属于对象所有者,只有其显式授权后其他用户才可访问。
在创建同义词vbisal时指定了public关键字,视图vbisal是当前用户,因此这两个对象其实还是属于不同空间的,不存在同名问题,
SQL> create view v$bisal as select * from x$bisal;
View created.
SQL> create public synonym v$bisal for v_$bisal;
Synonym created.
再测试几个例子,不能创建两个同名的公共同义词,
SQL> create public synonym a for dba_tables;
Synonym created.
SQL> create public synonym a for dba_tables;
create public synonym a for dba_tables
*
ERROR at line 1:
ORA-00955: name is already used by an existing object
不能创建两个同名的私有同义词,
SQL> create synonym a for dba_tables;
Synonym created.
SQL> create synonym a for user_tables;
create synonym a for user_tables
*
ERROR at line 1:
ORA-00955: name is already used by an existing object
但是可创建同名的公共同义词和私有同义词,
SQL> create public synonym a for dba_tables;
Synonym created.
SQL> create synonym a for dba_tables;
Synonym created.
SQL> select object_name, object_type from dba_objects where object_name in ('A');
OBJECT_NAME OBJECT_TYPE
--------------- -----------------------
A SYNONYM
A SYNONYM
但是当前用户下,不能再创建同名对象,无论是视图、序列、还是表,
SQL> create table a(id number);
create table a(id number)
*
ERROR at line 1:
ORA-00955: name is already used by an existing object
SQL> create sequence a;
create sequence a
*
ERROR at line 1:
ORA-00955: name is already used by an existing object
SQL> create view a as select * from user_tables;
create view a as select * from user_tables
*
ERROR at line 1:
ORA-00955: name is already used by an existing object