昨天的文章《v$和v_$的一些玄机》,有朋友提出了一些异议,如下相同名称的两个对象v$bisal,Oracle是怎么辨认的?或者说我们的访问请求,Oracle是怎么知道调用的是谁?这里确实存在一些模糊的地方,梳理一下。

文章提到了,

"(1) 假设x$bisal是从dba_tables复制过来的,
create table x$bisal as select * from dba_tables;
(2) 创建视图v$bisal,他的基表是x$bisal,
create view v$bisal as select * from x$bisal;
(3) 创建视图v_$bisal,他是基于v$bisal视图创建的,
create view v_$bisal as select * from v$bisal;
(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条件,限定在当前实例的信息,例如v$session基于gv$session,增加了where inst_id=userenv('Instance'),

同名的同义词和视图解惑_sql

gv$session定义(未显示完整),

同名的同义词和视图解惑_mysql_02

针对普通用户自定义的视图,我们稍微改造下最开始的SQL,

1. x$bisal表包含id和a字段。

1. v$bisal视图是基于x$bisal表,包含id和a字段。

2. v_$bisal视图是基于v$bisal视图,但是只含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

v$bisal这个名称,既是视图的名称,又是公共同义词的名称。问题来了,执行检索v$bisal,访问的是视图还是公共同义词?

公共同义词v$bisal指向的是含一个字段的视图v_$bisal,视图v$bisal是含两个字段的。我们执行的“desc v$bisal”,返回两个字段,所以可确认他访问的是视图v$bisal,说明在进行数据访问的时候,先访问的是视图,其优先级要高于公共同义词,

SQL> desc v$bisal
 Name          Null?    Type
 ------------- -------- ----------------------------
 ID                     NUMBER
 A                      VARCHAR2(1)

如下是我们创建的顺序,

x$bisal(表) -> v$bisal(视图) -> v_$bisal(视图) -> v$bisal(同义词)

但实际执行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同义词属于对象所有者,只有其显式授权后其他用户才可访问。

在创建同义词v$bisal时指定了public关键字,视图v$bisal是当前用户,因此这两个对象其实还是属于不同空间的,不存在同名问题,

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