Dual表到底是怎么回事

来源:互联网 发布:淘宝上p一张图多少钱 编辑:程序博客网 时间:2024/04/26 07:23

Dual表是ORACLE里比较特殊的表,在很多时候都能看到它。例如,我们要取系统时间:

SQL> select sysdate from dual;

SYSDATE
-----------
2004-12-15

但Dual表到底是怎么回事,我倒是想仔细探究一番。恰好,某日CSDN社区ORACLE板块上,有网友把Dual表删了,这个更引起了我探究的兴趣。

先来看看系统里有多少对象是和Dual表有关的:

SQL> select OWNER,OBJECT_NAME,OBJECT_TYPE,TO_CHAR(CREATED,'YYYY-MM-DD') CREATEDATE from all_objects where object_name like '%DUAL%';

OWNER                          OBJECT_NAME                    OBJECT_TYPE        CREATEDATE
------------------------------ ------------------------------ ------------------ ----------
SYS                            DUAL                           TABLE              2000-02-27
PUBLIC                         DUAL                           SYNONYM            2000-02-27

可以看到在系统中存在一个DUAL的表,还有一个DUAL的同义词。

DUAL表的所有者是SYS,因为,DUAL是系统创建的,而SYS是根用户。而所有者为PUBLIC的SYNONYM则让DUAL对所有用户都是透明的,不需要加“SYS”前缀。

再来看一下,DUAL表的结构:
create table SYS.DUAL
(
  DUMMY VARCHAR2(1)
)
tablespace SYSTEM
  pctfree 10
  pctused 40
  initrans 1
  maxtrans 255
  storage
  (
    initial 16K
    next 16K
    minextents 1
    maxextents 505
    pctincrease 50
  );
DUAL表中仅有1列:DUMMY,为VARCHAR2(1)型。(很是困惑,ORACLE为什么要用VARCHAR(1)型,用CHAR(1)难道不好么?)从这样的表结构来看,DUAL表设计的目的就是要尽可能的简单,以减少检索的开销。
还有,DUAL表是建立在SYSTEM表空间的,第一是因为DUAL表是SYS这个用户建的,本来默认的表空间就是SYSTEM;第二,把这个可能经常被查询的表和用户表分开来存放,对于系统性能的是有好处的。

有了创建了表、创建了同义词还是不够的。DUAL在SYS这个Schema下面,因此用别的用户登录是无法查询这个表的,因此还需要授权:
grant select on SYS.DUAL to PUBLIC with grant option;
将Select 权限授予公众。

接下来看看DUAL表中的数据,事实上,DUAL表中的数据和ORACLE数据库环境有着十分重要的关系(ORACLE不会为此瘫痪,但是不少存储过程以及一些查询将无法被正确执行)。
SQL> select * from dual;

DUMMY
-----
X

在创建数据库之后,DUAL表中便已经被插入了一条记录。个人认为:DUMMY字段的值并没有什么关系,重要的是DUAL表中的记录数。
下面,试着把DUAL表中的数据删除,看看会出现什么结果:
SQL> delete from dual;

1 行 已删除

SQL> select * from dual;

DUMMY
-----

看起来很正常,那么来取一下系统时间来看看:
SQL> select sysdate from dual;

SYSDATE
-----------

这下子,我们便取不到系统日期了。因为,sysdate是个函数,作用于每一个数据行。现在没有数据了,自然就不可能取出系统日期。
这个对于很多用
select sysdate into v_sysdate from dual;
这种方式取系统时间以及其他信息的存储过程来说是致命的,因为,ORACLE会马上抛出一个NO_DATA_FOUND(ORA-01403)的异常,即使异常被捕获,存储过程也将无法正确完成要求的动作。

反过来,如果我们再插入一条数据,那会是什么结果呢?
SQL> insert into dual values('Y');

1 行 已插入

SQL> commit;

提交完成

SQL> select * from dual;

DUMMY
-----
X
Y
SQL> select sysdate from dual;

SYSDATE
-----------
2004-12-15
2004-12-15

这个时候返回的是两条记录,这样同样会引起问题。在通过使用

select sysdate into v_sysdate from dual;

来获取时间或者其他信息的存储过程来说,ORACLE会抛出TOO_MANY_ROWS(ORA-01422)异常。

因此,需要保证在DUAL表内有且仅有一条记录。当然,也不能把DUAL表的UPDATE,INSERT,DELETE权限随意释放出去,这样对于系统是很危险的。

最后,总结一下如果DUAL表被“不幸”删除后的恢复:
1. 以SYS登录。
2. 创建DUAL表(SQL如上述)。
3. 授予公众SELECT权限(SQL如上述,但不要给UPDATE,INSERT,DELETE权限)。
4. 向DUAL表插入一条记录(仅此一条): insert into dual values('X');
5. 提交修改。

其他:
对于如此重要的表,ORACLE不予以保护确实令我想不通(针对8i),但是对于DELETE操作来说,ORACLE却似乎在后面做了些什么。
SQL> select * from dual;

DUMMY
-----
X
Y

SQL> delete from dual;

1 行 已删除

SQL> commit;

提交完成

SQL> select * from dual;

DUMMY
-----
Y

不管表内有多少记录(没有记录除外),ORACLE对于每次DELETE操作都只删除了一条数据。这个又是为什么呢??

关于优化问题:
建立一个索引,可能是有用的,但是,如果能把DUAL表放入SGA是不是更好??可能不是,因为我们几乎从来都不关心DUMMY这个列是什么值(这次除外),ORACLE也不会关心的。


PS:
DUAL在英语里是双重的意思,取这个表名确实不好理解(可能是个人智商问题),DUMMY是虚的意思,这个倒是好理解,就当它没有吧…………
欢迎大家指教!