Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1369954
  • 博文数量: 172
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 3831
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-30 13:00
个人简介

About me:Oracle ACE pro,optimistic,passionate and harmonious. Focus on ORACLE,MySQL and other database programming,peformance tuning,db design, j2ee,Linux/AIX,Architecture tech,etc

文章分类

全部博文(172)

文章存档

2024年(27)

2023年(28)

2022年(43)

2020年(62)

2014年(3)

2013年(9)

分类: Oracle

2022-04-11 11:16:45

DUAL表的神秘之处

DUAL表是Oracle中特殊的数据字典表,它是由Oracle内部代码维护的,我们一般情况下看到的DUAL表单行单列,字段名为DUMMY,类型VARCHAR2(1),这些都是表象,因为DUAL表在Oracle中的用途很广泛,使用很便利,所以Oracle考虑了多方面因素,才造了这么个神奇的“虚表”。你认为DUAL表真的是单行单列的吗?先看表象:

SYS>desc dual

名称 是否为空类型

---------- -------- --------------

DUMMY VARCHAR2(1)


SYS>select * from dual;


DU

--

x


已选择 1 行。

表面来看,的确是单行单列,TOM(TOM KYTE,Oracle VP,世界知名数据库专家)曾经说DUAL表之所以做成单行单列只是为了便利,当然DUAL表有更多的用途,比如一些系统操作,如DROP TABLE,一些系统包等都可能会用到DUAL表,你改变了DUAL表的属性,比如增强一行,那么可能会导致严重的问题,数据字典都是不能乱动的。DUAL表的数据类型和列名以及它的值并没有绝对的关系,这个要注意,DUAL表用途广泛,Oracle优化器看到DUAL表,它就知道DUAL表应该是单行单列的,而且会启用相关优化措施(这点会在后面的例子中有说明)。那么DUAL表真的是单行单列的吗?看TOM给的一点提示:

--关闭数据库后,神奇的DUAL不同了

SYS>alter database close;


数据库已更改。


SYS>desc dual

名称 是否为空类型

---------- -------- --------------

DUMMY VARCHAR2(1)


SYS>select * from dual;


ADDR INDX INST_ID DU

-------- ---------- ---------- -- ---------- ------

03674AD4 0 1 X


已选择 1 行。

DUAL表既然是属于SYS数据字典,那么普通用户怎么访问到DUAL呢,而且还不要加SCHEMA,那当然是利用PUBLIC同义词了,查看:

SYS> SELECT * FROM DBA_SYNONYMS WHERE TABLE_NAME='DUAL';


OWNER SYNONYM_NAME TABLE_OWNER TABLE_NAME DB_LINK

-----------------------------------------------------------------------

PUBLIC DUAL SYS DUAL


已选择 1 行。

从上面可以看到,DUAL表并没有那么简单。它拥有很多让你amazing的特性,当然如果你动了它,它会让你更加地amazing。下面就开始DUAL表的探秘之旅吧。

1)9i10g下对DUAL进行DDLDML操作的神奇表现

前面已经说了,DUAL表这种特殊的数据字典,你只可以使用同义词来进行查询操作(当然所有的数据字典都不应该乱动),而不能进行其他的操作改变DUAL表的基本属性,如果DUAL表属性一旦发生变化,可能会造成很多意想不到的问题,下面就暴力改变下DUAL表的属性,看看DUAL表到底有哪些神奇之处(切记,不要在生产环境下实验,后果自负)

先在9i下进行相关实验(增加插入一行,然后SELECT 'X' FROM DUAL看看有几行,增加游标迭代):

--select * from dualselect dummy from dual9i10g下的表现一样,这个不用测试了

SQL>SHOW REL

release 902000400


SQL> select * from dual;


D

-

X


1 row selected.


--DUAL表插入一行

SQL> insert into dual values('Y');


1 row created.


--查看全部DUAL表内容,so amazing,竟然只返回新插入的一行

SQL> select * from dual;


D

-

Y


1 row selected.


--count查看全部记录数量,正确返回,与上面的对比,DUAL表在SQL*PLUS里的表现真是很让人迷惑

SQL> select count(*) from dual;


COUNT(*)

----------

2


1 row selected.


SQL> commit;


Commit complete.

已经演示了一些DUAL表的神奇表现,下面神奇继续上演:

--全部清空,再次amazing,明明两行,竟然只删除了一行后插入的,当然和上面的select不谋而合

SQL> delete from dual;


1 row deleted.


SQL> select * from dual;


D

-

X


1 row selected.


--继续删除,这下清净了

SQL> delete from dual;


1 row deleted.


SQL> select * from dual;


no rows selected


SQL> select count(*) from dual;


COUNT(*)

----------

0


1 row selected.


--不能乱删的,恢复吧

SQL> insert into dual values('X');


1 row created.


SQL> select * from dual;


D

-

X


1 row selected.


--我已经不相信select * from dual了,还是count一下保险点,正确

SQL> select count(*) from dual;


COUNT(*)

----------

1


1 row selected.


--对上面的演示继续,这次不用delete from dual了,用delete from dual where dummy='Y',直接删除掉即可

SQL> insert into dual values('Y');


1 row created.


SQL> commit;


Commit complete.


SQL> select count(*) from dual;


COUNT(*)

----------

2


1 row selected.


SQL> delete from dual where dummy='Y';


1 row deleted.


SQL> commit;


Commit complete.


SQL> select count(*) from dual;


COUNT(*)

----------

1


1 row selected.


--TRUNCATEUPDATE没有什么神奇的

--TRUNCATE操作,和DELETE FROM不同,TRUNCATE9.2.4下能清楚全部DUAL数据

--10g版本测试,TRUNCATE,UPDATE,DELETE FROM表现和9i一样,但是也有不一样的,请见10g版本测试

SQL> insert into dual values('Y');


1 row created.


SQL> commit;


Commit complete.


SQL> truncate table dual;


Table truncated.


SQL> select * from dual;


no rows selected


SQL> select count(*) from dual;


COUNT(*)

----------

0


--恢复,执行UPDATE,可以改变其值

SQL> insert into dual values('X');


1 row created.

Commit complete


SQL> update dual set dummy='Y';


1 row updated


SQL> commit;


Commit complete


SQL> select * from dual;


DUMMY

-----

Y


--恢复

SQL> update dual set dummy='X';


1 row updated


SQL> commit;


Commit complete

上面演示了在9.2.4下对DUALINSERT,DELETE,TRUNCATE,UPDATE操作,可以看出,DUAL表的确很神奇,当然在SQL*PLUS里新插入一上用SELECT * FROM DUAL是看不出到底有几行的,用COUNT函数可以,在测试中,我发现在PLSQL DEVELOPER工具中用SELECT * FROM DUAL是可以看出到底有多少行的,说明SQL*PLUSDUAL还是紧密相关的。

DUAL表多插入了或清空了,那么有什么不利之处呢?不利之处太多了,可能使系统包失效,使一些DDL操作报错,使一些程序报错等。下面就继续在9.2.4下,用简单的存储过程测试下DUAL表多插入了或清空了导致的问题。

SQL> select * from dual;

D

-

X

已选择 1 行。

已用时间: 00: 00: 00.03

SQL> insert into dual values('Y');

已创建 1 行。

已用时间: 00: 00: 00.07

SQL> commit;

以上测试只是9.2.0.4的情况,下面回到本章10g版本,你会发现与9.2.0.4很多不同的地方。

SQL>show rel

release 1002000100

SQL>select * from dual;


DU

--

X


已选择 1 行。


SQL>insert into dual values('Y');


已创建 1 行。


SQL>commit;


提交完成。


SQL>select * from dual;


DU

--

Y


已选择 1 行。


--注意和9i测试的对比,10.2就算插入了一行记录,count(*)仍然不能正确显示行数,还是1行,9i的为2

SQL>select count(*) from dual;


COUNT(*)

----------

1


已选择 1 行。


--这个在9i显示的是两行,但是10.21行,可以猜测10.2多插入了行,在PL/SQL里的select sysdate into .. from dual

--也不会出问题,这个应该属于10g的优化

SQL>select sysdate from dual;


SYSDATE

--------------

31-10月-10


已选择 1 行。


--从上面可以看出,多插入一行,对select xxx into variable from dual是没有影响的。这个在9i下是要报too_many_rows错误--

DINGJUN123>declare

2 var_date date;

3 begin

4 select sysdate into var_date from dual;

5 dbms_output.put_line(var_date);

6 end;

7 /

31-10月-10


PL/SQL 过程已成功完成。


--DELETE FROM 9i一样,只能删除一行

SQL>delete from dual;


已删除 1 行。


SQL>commit;


提交完成。


SQL>select * from dual;


DU

--

X


已选择 1 行。


SQL>delete from dual;


已删除 1 行。


SQL>commit;


提交完成。


--DUAL表全清空了,COUNT还是只显示1行,DUAL表除了select * from dualselect dummy from dual无行显示外,--计算是正常的1

--这个和上面的多插入类似,9i也不同

SQL>select count(*) from dual;


COUNT(*)

----------

1


已选择 1 行。


SQL>select * from dual;


未选定行


SYS>select dummy from dual;


未选定行


SQL>select sysdate from dual;


SYSDATE

--------------

31-10月-10


已选择 1 行。


--DUAL表没有数据了同样不影响select xxx into variable from dual9i下测试是报no_data_found异常的

DINGJUN123>declare

2 var_date date;

3 begin

4 select sysdate into var_date from dual;

5 dbms_output.put_line(var_date);

6 end;

7 /

31-10月-10


PL/SQL 过程已成功完成。


--TRUNCATE正常截断,和9i一样,当然和上面一样,空DUAL表的COUNT和相关计算不受影响

SQL>insert into dual values('X');


已创建 1 行。


SQL>insert into dual values('Y');


已创建 1 行。


SQL>commit;


提交完成。


SQL>truncate table dual;


表被截断。


SQL>select * from dual;


未选定行


--恢复

SQL>insert into dual values('X');


已创建 1 行。


SQL>commit;


提交完成。

下面看看10g多插入表对一些DROP TABLE操作的影响,当然其他的也可能影响(比如DROP TABLESPACE),没有测试而已,测试的目的是说明,DUAL表不可欺。

--多插入一条数据语句省略

DINGJUN123>drop table t;

drop table t

*

 1 行出现错误:

ORA-00604: 递归 SQL 级别 1 出现错误

ORA-01422: 实际返回的行数超出请求的行数

返回ORA-00604ORA-01422错误,当对表进行DROP的时候,看到这两个错误,就要想起是DUAL表里的数据多了的缘故。通过SQL TRACE可以看到:

select dummy

from

dual where ora_dict_obj_type = 'TABLE'

说明DUAL表和DROP TABLE操作密切相关,此外DROP TABLESPACE也类似,当然可能有其他的操作也相关,这里不测试了。

9i好检查DUAL表数据是不是多了,一个COUNT搞定,在10g下测试COUNT还是返回一条,那么怎么办呢?有很多方式可以看到DUAL表的真正记录数,如到PLSQL DEVELOPER工具下查看,或用PL/SQL的游标解决。如下:

1)PL/SQL DEVELOPER可以正确显示10g下的DUAL表记录数

2)采用游标迭代

为什么不直接在PL/SQLselect sysdate into..等方式查看呢?前面测试了,10g下的DUAL不管是数据多了还是数据少了,select sysdate into … from dual显示的都是1行,而select dummy into … from dual如果清空了则不返回数据,多了的话也只显示1行,所以用迭代的方式。

DINGJUN123>select count(*) from dual;


COUNT(*)

----------

1


已选择 1 行。


DINGJUN123>set serveroutput on


DINGJUN123>begin

for rec in (select dummy from dual) loop

3 dbms_output.put_line(rec.dummy);

4 end loop;

5 end;

6 /

X

Y


PL/SQL 过程已成功完成。

通过测试9i10g相应版本的DUAL操作,可以看出,9i10g变化还是挺大的,9i下多插入或清空了DUAL表数据影响SELECT COUNT(*) FROM DUAL以及类似于SELECT SYSDATE FROM DUAL之类的操作,多插入了不影响DROP TABLE等操作。但是在10.2的测试下可以看出,多插入或清空不影响上述COUNT和计算操作,但是多插入了,则影响DROP TABLE等操作(清空了不影响DROP TABLE操作),当然清空了影响SELECT * FROM DUALSELECT DUMMY FROM DUAL,但是不影响类似于SELECT SYSDATE FROM DUAL。此外,10g下在PL/SQL里用隐式游标迭代查找DUAL表里的数据,和9i表现一样。

3)10gFAST DUAL优化

9i下的DUAL表访问没有优化,一般逻辑读为310gDUAL表如果不需要访问DUAL表里的内容,而是利用DUAL表的一些功能,比如select sysdate from dual则会采用FAST DUAL方式来访问表,它不是真正的访问表,因此逻辑读为0,这样在一个高频DUAL表使用的系统中,会大量减少逻辑读。

9iDUAL的访问方式是:

--不管是只访问dual表本身的内容,还是用于计算等,都是全表扫描DUAL表,返回逻辑读一般为3个,无FAST DUAL

SQL> set autotrace traceonly

SQL> select * from dual;


Execution Plan

----------------------------------------------------------

0 SELECT STATEMENT Optimizer=CHOOSE

1 0 TABLE ACCESS (FULL) OF 'DUAL'


Statistics

----------------------------------------------------------

0 recursive calls

0 db block gets

3 consistent gets

0 physical reads

0 redo size

487 bytes sent via SQL*Net to client

655 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

1 rows processed


SQL> select sysdate from dual;


Execution Plan

----------------------------------------------------------

0 SELECT STATEMENT Optimizer=CHOOSE

1 0 TABLE ACCESS (FULL) OF 'DUAL'


Statistics

----------------------------------------------------------

0 recursive calls

0 db block gets

3 consistent gets

0 physical reads

0 redo size

495 bytes sent via SQL*Net to client

655 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

1 rows processed

10g则不同,如果只是利用DUAL表查找比如SYSDATE,做些相关计算,比如1+1,那么Oracle优化器(CBO,RBO一样规则)采用FAST DUAL方式访问,它不需要真正访问DUAL表,因此逻辑读为0

DINGJUN123>set autotrace traceonly

--访问DUAL表本身的数据,不能走FAST DUAL,因为它需要真正访问表了

DINGJUN123>select * from dual

2 ;


已选择 1 行。



执行计划

----------------------------------------------------------

Plan hash value: 272002086


--------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

--------------------------------------------------------------------------

| 0 | SELECT STATEMENT | | 1 | 2 | 2 (0)| 00:00:01 |

| 1 | TABLE ACCESS FULL| DUAL | 1 | 2 | 2 (0)| 00:00:01 |

--------------------------------------------------------------------------



统计信息

----------------------------------------------------------

1 recursive calls

0 db block gets

3 consistent gets

0 physical reads

0 redo size

409 bytes sent via SQL*Net to client

384 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

1 rows processed


--用于取系统函数,计算,启用FAST DUAL,不需要真正访问表,逻辑读为0

DINGJUN123>select sysdate from dual;


已选择 1 行。



执行计划

----------------------------------------------------------

Plan hash value: 1388734953


-----------------------------------------------------------------

| Id | Operation | Name | Rows | Cost (%CPU)| Time |

-----------------------------------------------------------------

| 0 | SELECT STATEMENT | | 1 | 2 (0)| 00:00:01 |

| 1 | FAST DUAL | | 1 | 2 (0)| 00:00:01 |

-----------------------------------------------------------------



统计信息

----------------------------------------------------------

1 recursive calls

0 db block gets

0 consistent gets

0 physical reads

0 redo size

417 bytes sent via SQL*Net to client

384 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

1 rows processed


--这个也是访问了DUAL

DINGJUN123>select dummy,sysdate from dual;


已选择 1 行。



执行计划

----------------------------------------------------------

Plan hash value: 272002086


--------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

--------------------------------------------------------------------------

| 0 | SELECT STATEMENT | | 1 | 2 | 2 (0)| 00:00:01 |

| 1 | TABLE ACCESS FULL| DUAL | 1 | 2 | 2 (0)| 00:00:01 |

--------------------------------------------------------------------------



统计信息

----------------------------------------------------------

1 recursive calls

0 db block gets

3 consistent gets

0 physical reads

0 redo size

474 bytes sent via SQL*Net to client

384 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

1 rows processed



--RBO虽然不用了,还是测试一下,规则与CBO一样

DINGJUN123>alter session set optimizer_mode=rule;


会话已更改。


DINGJUN123>select 1 from dual;


已选择 1 行。



执行计划

----------------------------------------------------------

Plan hash value: 1388734953


---------------------------------

| Id | Operation | Name |

---------------------------------

| 0 | SELECT STATEMENT | |

| 1 | FAST DUAL | |

---------------------------------


Note

-----

- rule based optimizer used (consider using cbo)



统计信息

----------------------------------------------------------

1 recursive calls

0 db block gets

0 consistent gets

0 physical reads

0 redo size

406 bytes sent via SQL*Net to client

384 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

1 rows processed


DINGJUN123>select sysdate,sysdate,sysdate from dual;


已选择 1 行。



执行计划

----------------------------------------------------------

Plan hash value: 1388734953


---------------------------------

| Id | Operation | Name |

---------------------------------

| 0 | SELECT STATEMENT | |

| 1 | FAST DUAL | |

---------------------------------


Note

-----

- rule based optimizer used (consider using cbo)



统计信息

----------------------------------------------------------

1 recursive calls

0 db block gets

0 consistent gets

0 physical reads

0 redo size

547 bytes sent via SQL*Net to client

384 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

1 rows processed


DINGJUN123>select dummy from dual;


已选择 1 行。



执行计划

----------------------------------------------------------

Plan hash value: 272002086


----------------------------------

| Id | Operation | Name |

----------------------------------

| 0 | SELECT STATEMENT | |

| 1 | TABLE ACCESS FULL| DUAL |

----------------------------------


Note

-----

- rule based optimizer used (consider using cbo)



统计信息

----------------------------------------------------------

1 recursive calls

0 db block gets

3 consistent gets

0 physical reads

0 redo size

409 bytes sent via SQL*Net to client

384 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

0 sorts (memory)

0 sorts (disk)

1 rows processed

10g有了FAST DUAL优化,增强了DUAL表作为常用工具的功能,这也是Oracle10g新版本带来的众多新特性之一,值得注意。



阅读(907) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~