某客户的一个表在进行分析和建索引一段时间时,均报ORA-8103错误。

让我们先看看ORA-8103错误是个什么样的错误?

[oracle@xty ~]$ oerr ora 8103
08103, 00000, "object no longer exists"
// *Cause: The object has been deleted by another user since the operation
// began, or a prior incomplete recovery restored the database to
// a point in time during the deletion of the object.
// *Action: Delete the object if this is the result of an incomplete
// recovery.

从上面的信息来看,引起这个错误的并不是由于什么对象被删除或者不完全恢复引起的。看来剩下的可能就是BUG或者是坏块了。

select /*+ parallel(a 6) no_index(a) */
count(lrrq) from hz2004.HJXX_RYZPXXB a

由于表很大,100g左右,因此等了好长一段时间,就报了ORA-8103错误。由于不同的SQL语句(建索引、分析表和查询表数据)都报了这个错误,基本可以确定是坏块引起的问题。

由于表中有long raw类型,因此不能通过从主键取得ROWID再根据ROWID读到值以后插入新表,由于查询表有问题,导出也不会成功。因此决定想办法来修复这个问题:

首先要确定是哪个块出现问题:

SQL> alter session set max_dump_file_size=unlimited;
SQL> alter session set db_file_multiblock_read_count=1;
SQL> alter session set events ‘immediate trace name trace_buffer_on level 1048576′;
SQL> alter session set events ‘10200 trace name context forever, level 1′;
SQL> select /*+ no_index(a */ count(*) from hz2004.hjxx_ryzpxxb a;

(注:经测试,在10g中,需要去掉trace_buffer_on那一个语句才会有trace,否则没有trace文件)
再次报错时,检查trace文件,发现如下的信息:

Consistent read started for block 11 : 09c63a9e
env: (scn: 0×0886.245ea878 xid: 0×0000.000.00000000
uba: 0×00000000.0000.00 statement num=0 parent xid:
xid: 0×0000.000.00000000
scn: 0×0000.00000000 0sch: scn: 0×0000.00000000)

从trace文件中可以发现在读文件号39块号408222时报错。

执行:

select * from hz2004.hjxx_ryzpxxb
where rowid=dbms_rowid.rowid_create(1,7415,39,408222,0);

报ORA-08103错误,可以确认是39号文件408222块坏。

然而用dbv命令检查39号文件,没有发现坏块。

通过dd取出39号文件408222块,发现该块居然是一个未格式化的块,但是该块在表的High water mark以下。单从该块来说是一个好的块,所以dbv检查并不会报坏块。

既然如此,我就故意做成一个坏块,然后让oracle跳过这个坏块。关闭库之后,用dd把坏块复制出来保存在一个文件,然后修改该块,使之成为一个坏块(很多的方法,这里我的方法就是修改其checksum值)。然后用dd复制回去。重启库之后,用 dbms_repair.skip_corrupt_blocks过程设置表在读取数据时,跳过坏块。

再次读取该表的所有数据,不再报错。

客户进行建索引和分析时也不再报错,顺利进行。

当然后续的工作,还是需要将数据导出来再导入,这样更稳妥。

在Oracle 10g 的database concepts上提到,Oracle 10g的最大容量是8exabytes(EB),1EB=1024PB=1048576TB,这是一个相当大的数据了。还没有听说过单个库的容量达到这么大的。(虽然现实中有达到EB级别的数据量的,但那是很多个库的数据量的合计,而不是单个库。)

我们看看这8EB是怎么计算来的:

Oracle 10g的数据文件最多可以有64K个,对于单个数据文件来说,BigFile表空间的数据文件可以达到2的32次方个块,也就是4G块(因为rdba为32位)。每个块的最大大小为32K,因此一个BigFile表空间数据文件的最大大小为4G*32K=128T。整个库的最大大小即为64K(数据文件数)* 128T(单个文件最大容量)=8192PB=8EB

先看看Oracle文档上对db_block_checking参数的说明:

DB_BLOCK_CHECKINGcontrols whether Oracle performs block checking for data blocks. When this parameter is set totrue, Oracle performs block checking for all data blocks. When it is set tofalse, Oracle does not perform. block checking for blocks in the user tablespaces. However, block checking for theSYSTEMtablespace is always turned on.

Oracle checks a block by going through the data on the block, making sure it is self-consistent. Block checking can often prevent memory and data corruption. Block checking typically causes 1% to 10% overhead, depending on workload. The more updates or inserts in a workload, the more expensive it is to turn on block checking. You should set

DB_BLOCK_CHECKING to true

if the performance overhead is acceptable

从文档中可以看到,DB_BLOCK_CHECKING参数主要是用于数据块的逻辑(一致)检查(但只是块内,不包括块间的逻辑检查,比如索引项目的ROWID指向的是不存在的行等)。主要用于防止在内存中损坏或数据损坏。由于是逻辑检查,因此引起的额外负荷比较高,甚至可以达到10%,因此对于一个繁忙的系统,特别是插入或更新操作很多的系统,性能影响是比较明显的。

该参数对SYSTEM表空间始终是处于“打开”状态,而不管该参数是否设置为FALSE。

下面再看看db_block_checksum参数的说明:

DB_BLOCK_CHECKSUMdetermines whether DBWnand the direct loader will calculate achecksum(a number calculated from all the bytes stored in the block) and store it in the cache header of every data block when writing it to disk. Checksums are verified when a block is read-only if this parameter istrueand the last write of the block stored a checksum. In addition, Oracle gives every log block a checksum before writing it to the current log.

If this parameter is set tofalse, DBWncalculates checksums only for theSYSTEMtablespace, but not for user tablespaces.

Checksums allow Oracle to detect corruption caused by underlying disks, storage systems, or I/O systems. Turning on this feature typically causes only an additional 1% to 2% overhead. Therefore, Oracle Corporation recommends that you setDB_BLOCK_CHECKSUM to true

 

可以看到,DB_BLOCK_CHECKSUM只是在写入(DBWn常规写法或用户进程直接路径写入),根据一个CHECKSUM算法,计算数据块的校验和。然后写入数据块的一个特定位置(CACHE HEADER,具体是块的16-17字节,以0字节起算)。在读取块时,再进行检验。主要是防止IO硬件和IO子系统的错误。

 

CHECKSUM的算法只是根据块的字节值计算一个效验和,因此算法比较简单,引起的系统额外负荷通常在1%-2%

实际上,即使将该参数设为TRUE,将数据块(包括SYSTEM表空间)的16-17字节清0,同时将15字节(flag),第3位(即值为16进制 0x04)清为0,则在块读取时也不会做CHECKSUM检查。如果该参数为FALSE,对于除SYSTEM的其他表空间,如果原来有CHECKSUM 值,将15-16字节清0也不会做CHECKSUM检查。

在前一篇文章(Oracle的文件号、相对文件号及其他)中,我们提到Oracle数据文件的相对文件号为1-1023。然而从10g开始,可以使用大文件(BIGFILE)的表空间,这种表空间只能使用1个数据文件。之所以称为大文件,是因为这种文件可以很“大”,最多可以有4G个块和128TB的大小。

大文件表空间的数据文件,其相对文件号都统一为1024,用下面的测试可以验证:

SQL*Plus: Release 10.2.0.1.0 - Production on Thu Apr 3 23:11:51 2008

Copyright (c) 1982, 2005, Oracle.  All rights reserved.

Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options

SQL> select file_id,relative_fno,file_name,tablespace_name from dba_data_files;

   FILE_ID RELATIVE_FNO FILE_NAME                                TABLESPACE_NAME
---------- ------------ ---------------------------------------- --------------------
         1            1 /u02/oradata/xty/system01.dbf            SYSTEM
         2            2 /u02/oradata/xty/undotbs01.dbf           UNDOTBS1
         3            3 /u02/oradata/xty/sysaux01.dbf            SYSAUX
         4            4 /u02/oradata/xty/users01.dbf             USERS

SQL> create bigfile tablespace test1 datafile '/u02/oradata/xty/test01.dbf' size 2m;

Tablespace created.

SQL> select file_id,relative_fno,file_name,tablespace_name from dba_data_files;

   FILE_ID RELATIVE_FNO FILE_NAME                                TABLESPACE_NAME
---------- ------------ ---------------------------------------- --------------------
         1            1 /u02/oradata/xty/system01.dbf            SYSTEM
         2            2 /u02/oradata/xty/undotbs01.dbf           UNDOTBS1
         3            3 /u02/oradata/xty/sysaux01.dbf            SYSAUX
         4            4 /u02/oradata/xty/users01.dbf             USERS
         5         1024 /u02/oradata/xty/test01.dbf              TEST1

SQL> create bigfile tablespace test2 datafile '/u02/oradata/xty/test02.dbf' size 2m;

Tablespace created.

SQL> select file_id,relative_fno,file_name,tablespace_name from dba_data_files;

   FILE_ID RELATIVE_FNO FILE_NAME                                TABLESPACE_NAME
---------- ------------ ---------------------------------------- --------------------
         1            1 /u02/oradata/xty/system01.dbf            SYSTEM
         2            2 /u02/oradata/xty/undotbs01.dbf           UNDOTBS1
         3            3 /u02/oradata/xty/sysaux01.dbf            SYSAUX
         4            4 /u02/oradata/xty/users01.dbf             USERS
         5         1024 /u02/oradata/xty/test01.dbf              TEST1
         6         1024 /u02/oradata/xty/test02.dbf              TEST2

6 rows selected.

可以看到所有的“大”文件,其相对文件号均为1024。

每个大文件表空间只能有一个数据文件,尝试对大文件表空间增加数据文件,则会出现错误:

SQL> alter tablespace test1 add datafile '/u02/oradata/xty/test011.dbf' size 2m;
alter tablespace test1 add datafile '/u02/oradata/xty/test011.dbf' size 2m
*
ERROR at line 1:
ORA-32771: cannot add file to bigfile tablespace

Oracle10g 在AIX 5.3上的安装

今天无事,在单位的测试机上,安装了一下Oracle10g

环境:AIX 5.3 ML06 ,数据库版本 : Oracle 10g for AIX 5L 64bit

简单记录了一下安装过程:

   检查硬件配置
   检查内存:
   # lsattr -El sys0 -a realmem
   realmem 2097152 Amount of usable physical memory in Kbytes False
   检查交换空间
   # lsps -a
   Page Space      Physical Volume   Volume Group    Size %Used Active  Auto  Type
   hd6             hdisk0            rootvg         512MB     1   yes   yes    lv
   检查临时文件目录(至少应有400M空闲空间)
   df -k /tmp
   Filesystem    1024-blocks      Free %Used    Iused %Iused Mounted on
   /dev/hd3            65536     63984    3%       16     1% /tmp  
   检查操作系统位数
   # getconf HARDWARE_BITMODE
   64
   检查操作系统版本号
   # oslevel -r
   5300-06
   检查安装的软件包:
   # lslpp -l bos.adt.base bos.adt.lib bos.adt.libm bos.perf.perfstat bos.perf.libperfstat bos.perf.proctools bos.adt.prof bos.cifs_fs.rte
  Fileset                      Level  State      Description        
  ----------------------------------------------------------------------------
Path: /usr/lib/objrepos
  bos.adt.base              5.3.0.60  COMMITTED  Base Application Development
                                                 Toolkit
  bos.adt.lib               5.3.0.60  COMMITTED  Base Application Development
                                                 Libraries
  bos.adt.libm              5.3.0.60  COMMITTED  Base Application Development
                                                 Math Library
  bos.adt.prof              5.3.0.60  COMMITTED  Base Profiling Support
  bos.cifs_fs.rte           5.3.0.60  COMMITTED  Runtime for SMBFS
  bos.perf.libperfstat      5.3.0.60  COMMITTED  Performance Statistics Library
                                                 Interface
  bos.perf.perfstat         5.3.0.60  COMMITTED  Performance Statistics
                                                 Interface
  bos.perf.proctools        5.3.0.60  COMMITTED  Proc Filesystem Tools

Path: /etc/objrepos
  bos.cifs_fs.rte           5.3.0.60  COMMITTED  Runtime for SMBFS
  bos.perf.libperfstat      5.3.0.60  COMMITTED  Performance Statistics Library
                                                 Interface
  bos.perf.perfstat         5.3.0.60  COMMITTED  Performance Statistics
                                                 Interface

对于缺少的包,用installp -a -d /dev/cd0 包名  进行安装,安装时放入AIX的安装光备                                    

检查安装的补丁包
# /usr/sbin/instfix -i -k "IY58143 IY59386 IY60930 IY66513 IY70159"
    All filesets for IY58143 were found.
    All filesets for IY59386 were found.
    All filesets for IY60930 were found.
    All filesets for IY66513 were found.
    All filesets for IY70159 were found.

# /usr/sbin/instfix -i -k "IY68989"
    All filesets for IY68989 were found.

安装AIX C++ 8.0 运行时

编辑/etc/hosts文件
   
创建用户组oinstall,dba
mkgroup -'A' oinstall
mkgroup -'A' dba
创建用户oracle
修改oracle用户密码
确认nobody用户存在 id nobdy
配置shell limits( smit chuser)
  soft FILE size -1
  soft CPU time -1
  soft DATA segment -1
  soft STACK size -1
更改系统参数 smit chgsys
  将 Maximum number of PROCESSES allowed per user改为2048以上
 
创建目录
# mkdir -p /u01/app/oracle
# chown oracle:oinstall /u01/app/oracle
# chmod 775 /u01/app/oracle
# mkdir -p /u02/oradata
# chown oracle:oinstall /u02/oradata
# chmod 755 /u02/oradata

创建文件系统/u01 /u02
   
切换到oracle用户,在.profile中增加如下两行
umask 022
AIXTHREAD_SCOPE=S; export AIXTHREAD_SCOPE
ORACLE_BASE=/u01/app/oracle; export ORACLE_BASE

执行
  . ./.profile
将安装文件ftp到主机
解压文件
gunzip 10gr2_aix5l64_database.cpio.gz
cpio -idmv  10gr2_aix5l64_database.cpio (如果报0511-903 Out of phase这样的错误,cpio命令加上c 这个参数,即idmcv)                                                  
以root用户执行 Disk1/rootpre/rootpre.sh
切换回oracle用户
export DISPLAY
运行Disk1/runInstaller完成安装

完成后注意修改操作系统maxperm% maxclient% minperm% v_pinshm strict_maxclient strict_maxperm参数

前一天遇到一例database link问题,仅供大家参考:

数据库是运行在Windows 2003上的Oracle 9i

先来看看故障现象,维护人员发现数据库上的job不能正常执行。用TOAD手工调用job的存储过程,TOAD一直阻塞。

经过这个故障的过程如下:

1.      在TOAD中手工执行存储过程,TOAD挂起。
2. 用sqlplus连接到数据库,检查v$session_wait,发现执行存储过程的会话正在等待library cache pin。
3. 通过x$kglob与v$session_wait,发现等待library cache pin的SQL语句为:
         select flag from account@zw.world ....
4. 手工执行此SQL,客户端挂起。怀疑是数据链路问题,执行select * from dual@zw.world,客户端也挂起。
5. 在sqlplus下,新建数据库zw_link,执行select * from dual@zw_link,执行此SQL仍然会导致客户端挂起。
6. 怀疑数据库服务器不能正常接连到ZW库。在数据库服务器上用sqlplus连接ZW库:
       sqlplus xxx/xxx@zw
    这时数据库服务器(windows系统)弹出一对话框,windows木马防火墙提示是否允许sqlplus程序访问zw的IP地址的信息。看来是由于木马防火墙引起的问题。
7. 查看系统中运行的进程,发现windows木马防火墙软件是通过线程注入到其他进程的方式,对进程的网络访问进行控制。在进程要访问网络时,弹出对话框选择是否允许访问网络,但ORACLE进程是以服务方式运行的,没有设置为与桌面交互,所以弹 出的对话框不能显示,所以一直处于等待状态。
8. 去掉windows木马防火墙的自动启动,重启服务器,删除windows木马防火墙,检查数据库链接已经正常。
9. 手工执行存储过程,存储过程正常执行。检查存储过程运行产生的日志,也显示正常。至此问题解决。

看来现在的防病毒,防木马的工具也是越来越“厉害”了。

当GLOBAL_NAMES参数设置为TRUE时,使用DATABASE LINK时,DATABASE LINK的名称必须与被连接库的GLOBAL_NAME一致。下面做一个测试,在测试中,创建数据库链接的库为XJ(WINDOWS 2003 ORACLE 10g 10.2.0.1),被链接的库为DMDB(LINUX AS5 ORACLE 10g 10.2.0.1 RAC)

首先查看DMDB的相关配置:

SQL> show parameter global_names

NAME                                 TYPE        VALUE
------------------------------------ ----------- ---------
global_names                         boolean     FALSE
SQL> select * from global_name;;

GLOBAL_NAME
----------------------------------------------------------
DMDB

可以看到这个被链接的库其global_names参数为FALSE。

要创建数据库链接的库的配置:

SQL> show parameter global_names

NAME                                 TYPE        VALUE
------------------------------------ ----------- -------
global_names                         boolean     FALSE
SQL> select * from global_name;

GLOBAL_NAME
--------------------------------------------------------
XJ

然后做下面的操作:

SQL> create database link test_link connect to test identified by test using 'DMDB';

数据库链接已创建。

SQL> select * from dual@test_link;

D
-
X

可以看到数据库链接工作正常。

在DMDB库上将global_names设为TRUE:

SQL> alter system set global_names=true;

系统已更改。

在XJ库上再次查询,并新建一个DATABASE LINK再进行查询:

SQL> select * from dual@test_link;

D
-
X

SQL> create database link test_link2 connect to test identified by test using 'D
MDB';

数据库链接已创建。

SQL> select * from dual@test_link2;

D
-
X

此时可以看数据库链接工作正常。我们再将XJ库的global_names参数设置为TRUE:

SQL> alter system set global_names=true;

系统已更改。

SQL> select * from dual@test_link2;
select * from dual@test_link2
                   *
第 1 行出现错误:
ORA-02085: 数据库链接 TEST_LINK2 连接到 DMDB

SQL> select * from dual@test_link
select * from dual@test_link
                   *
第 1 行出现错误:
ORA-02085: 数据库链接 TEST_LINK 连接到 DMDB

而再次将XJ库的global_names设为FALSE,则数据库链接又可用了。
SQL> alter system set global_names=false;

系统已更改。

SQL> select * from dual@test_link;

D
-
X

SQL> select * from dual@test_link2;

D
-
X

再将DMDB库的global_names设为FALSE,数据库链接仍然可用:

在DMDB库上:

SQL> alter system set global_names=false;

系统已更改。

在XJ库上:

SQL> select * from dual@test_link;

D
-
X

可以看到,链接仍然可以用。
如果在DMDB库上创建链接到XJ库上,可以观察到同样的结果。
可以得出一个结论:global_names参数设置为TRUE(感谢Robert.Li指出的此前的错误),影响的是创建数据库链接的那个库对数据库链接的使用。也就是说,如果一个库(实例)的global_names参数设值为TRUE,则该库连接其他库的数据库链接,其名称必须要与被连接的库的global_name相同:

在XJ库上:

SQL> alter system set global_names=true;

系统已更改。

SQL> create database link dmdb connect to test identified by test using 'dmdb';

数据库链接已创建。

SQL> select * from dual@dmdb;

D
-
X
SQL> select * from dual@test_link;
select * from dual@test_link
*
第 1 行出现错误:
ORA-02085: 数据库链接 TEST_LINK 连接到 DMDB

SQL> select * from dual@test_link2;
select * from dual@test_link2
                   *
第 1 行出现错误:
ORA-02085: 数据库链接 TEST_LINK2 连接到 DMDB

如果在GLOBAL_NAMES设置为TRUE的情况下,如果要建多个数据库链接到同一个库,怎么办呢?因为数据库链接的名称必须与目标库的GLOBAL_NAME相同。可以按如下的方法:

SQL> create database linkdmdb@link1connect to test identified by test using 'dmdb';

数据库链接已创建。

SQL> create database linkdmdb@link2connect to test identified by test using 'dmdb';

数据库链接已创建。

SQL> select * fromdual@dmdb;

D
-
X

SQL> select * fromdual@dmdb@link1;

D
-
X

SQL> select * fromdual@dmdb@link2;

D
-
X

也就是在GLOBAL_NAME后面加上@再加上一个标识。这样就能够创建多个数据库链接到同一目标库上了。

另外在创建数据库链接时,不能给其他SCHEMA创建链接,这是因为数据库链接(database link)其名称可以含有'.'即点号。比如A用户想给B用户创建一个DBLINK名叫LINKB,CREATE DATABASE LINK B.LINKB ......, 这个语句将会实际创建一个A用户下的名为B.LINKB的数据库链接。

SET TRANSACTION READ ONLY类似于SERIALIZABLE事务隔离级别,在发布SET TRANSACTION READ ONLY起的所有SELECT语句,其结果均为同一个时间点一致,直至显式地发布了COMMIT或ROLLBACK命令或隐式提交(执行DDL)。这个时间点为SET TRANSACTION READ ONLY这个语句执行后的时间点。这个语句与SERIALIZABLE不同之处在于,在READ ONLY这个范围内,不能进行DML。以下用测试说明:

用TEST1用户开启两个会话

在会话一中:

SQL> create table t1 (a int );

Table created.

SQL> insert into t1 values (10);

1 row created.

SQL> commit;

Commit complete.

在会话二中:

SQL> select * from t1;

         A
----------
        10

SQL> set transaction read only;

Transaction set.

SQL> select * from t1;

         A
----------
        10
然后在会话一中插入一行数据,并提交:

SQL> insert into t1 values (20);

1 row created.

SQL> commit;

Commit complete.

在会话二中查看表t2的数据:

SQL> /

         A
----------
        10

SQL> /

         A
----------
        10

SQL> commit;

Commit complete.

SQL> select * from t1;

         A
----------
        10
        20

可以看到,虽然会话一已经插入了一条数据并提交了,但是查询时,仍然只能看到一条数据。在COMMIT之后,SET TRANSACTION READ ONLY作用结束,再查询T1,可以看到新插入的数据了。

我们再看一下,这个“时间点”是从第一个SELECT语句的时候还是SET TRANSACTION READ ONLY刚执行完的时候:

在会话二中:

SQL> set transaction read only;

Transaction set.

然后在会话一中:

SQL> insert into t1 values (30);

1 row created.

SQL> commit;

Commit complete.

在会话二中:

SQL> select * from t1;

         A
----------
        10
        20

可以看到,新插入的数据30是在会话二的SET TRANSACTION READ ONLY之后和SELECT之前插入的,但SELECT语句看不到这个数据,因此这个时间点是在执行完SET TRANSACTION READ ONLY之后,而不是第一个SELECT语句执行那一刻。

我们继续下面的测试:

在会话二中:

SQL> drop table t2;

Table dropped.

SQL> select * from t1;

         A
----------
        10
        20
        30

可以看到DROP语句之后,由于隐式提交,SET TRANSACTION READ ONLY作用范围结束,又可以查到新插入的数据。

SQL> set transaction read only;

Transaction set.

SQL> insert into t1 values (40);
insert into t1 values (40)
            *
ERROR at line 1:
ORA-01456: may not perform. insert/delete/update operation inside a READ ONLY
transaction
可以看到,在SET TRANSACTION READ ONLY之后,不能执行DML

注意:SYS用户并不受SET TRANSACTION READ ONLY的影响:

SQL> show user
USER is "SYS"
SQL> set transaction read only;

Transaction set.

SQL> delete from t1 where rownum=1;

1 row deleted.

SQL> commit;

Commit complete.

以上测试即证明了这一点。

EXP导出数据时,如果CONSISTEN参数设为TRUE,则EXP导出时,会先发布SET TRANSACTION READ ONLY,保证所有导出数据在同一时间点上的一致性。当然,如果事务频繁,导出的数据量又大,很可能会遭遇ORA-01555错误。由于SET TRANSACTION READ ONLY对SYS用户无效,用SYS用户导出时CONSISTENT设为TRUE,应该没有效果。有兴趣的朋友可以进行测试。

Primary key 与Unique Key都是唯一性约束。但二者有很大的区别:

1、Primary key的1个或多个列必须为NOT NULL,如果列为NULL,在增加PRIMARY KEY时,列自动更改为NOT NULL。而UNIQUE KEY 对列没有此要求。

2、一个表只能有一个PRIMARY KEY,但可以有多个UNIQUE KEY。

下面以测试说明:

SQL> create table t (a int,b int,c int,d int);

Table created.

SQL> desc t
 Name                                      Null?    Type
 ----------------------------------------- -------- -----------

 A                                                  NUMBER(38)
 B                                                  NUMBER(38)
 C                                                  NUMBER(38)
 D                                                  NUMBER(38)

SQL> alter table t add constraint pk_t primary key (a,b);

Table altered.

SQL> desc t
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------

 A                                         NOT NULL NUMBER(38)
 B                                         NOT NULL NUMBER(38)
 C                                                  NUMBER(38)
 D                                                  NUMBER(38)

可以看到A、B两个列都自动改为了NOT NULL

SQL> alter table t modify (a int null);
alter table t modify (a int null)
                      *
ERROR at line 1:
ORA-01451: column to be modified to NULL cannot be modified to NULL
可以看到,列A不允许改为NULL

SQL> alter table t drop constraint pk_t;

Table altered.

SQL> alter table t add constraint uk_t_1 unique (a,b);

Table altered.

SQL> desc t
 Name                                      Null?    Type
 ----------------------------------------- -------- -----------

 A                                                  NUMBER(38)
 B                                                  NUMBER(38)
 C                                                  NUMBER(38)
 D                                                  NUMBER(38)

我们看到列A又变回了NULL。

注意到,在删除主键时,列的NULLABLE会回到原来的状态。如果在创建主键后,对原来为NULL的主键列,显式设为NOT NULL,在删除主键后仍然是NOT NULL。比如在创建主键后,执行下面的操作,可以看到:

SQL> alter table t modify (b int not null);

Table altered.

SQL> alter table t drop constraint pk_t;

Table altered.

SQL> desc t
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------

 A                                                  NUMBER(38)
 B                                         NOT NULL NUMBER(38)
 C                                                  NUMBER(38)
 D                                                  NUMBER(38)

再做如下的实验:

SQL> drop table t;

Table dropped.

SQL> create table t (a int,b int,c int,d int);

Table created.

SQL> alter table t add constraint uk_t_1 unique (a,b);

Table altered.

SQL> alter table t add constraint uk_t_2 unique (c,d);

Table altered.

可以看到可以增加两个UNIQUE KEY。看看能不能增加两个主键:

SQL> alter table t add constraint pk_t primary key (c);

Table altered.

SQL> alter table t add constraint pk1_t primary key (d);
alter table t add constraint pk1_t primary key (d)
                                  *
ERROR at line 1:
ORA-02260: table can have only one primary key
由此可以看到一个表只能有一个主键。

SQL> alter table t drop constraint pk_t;

Table altered.

SQL> insert into t (a ,b ) values (null,null);

1 row created.

SQL> /

1 row created.

SQL> insert into t (a ,b ) values (null,1);

1 row created.

SQL> /
insert into t (a ,b ) values (null,1)
*
ERROR at line 1:
ORA-00001: unique constraint (SYS.UK_T_1) violated

SQL> insert into t (a ,b ) values (1,null);

1 row created.

SQL> /
insert into t (a ,b ) values (1,null)
*
ERROR at line 1:
ORA-00001: unique constraint (SYS.UK_T_1) violated

主键和唯一键约束是通过参考索引实施的,如果插入的值均为NULL,则根据索引的原理,全NULL值不被记录在索引上,所以插入全NULL值时,可以有重复的,而其他的则不能插入重复值。

个人认为ORACLE的学习,就好比武侠小说中学武功。要从三方面入手:

1、内功:针以学习ORACLE来说,内功就是对基本概念的掌握,ORACLE架构的深入理解,原理的掌握。如果有兴趣和时间,研究一下Internal的东西,这好比修习易筋经,需要极大的毅力和长期的坚持。

2、招式:如果光有内功,没有招式,则会陷入空有高深内力,却无从发招的尴尬。学习ORACLE也一样,还是需要掌握功能的使用,具体到SQL的使用,各个性能视图的使用,数据字典的使用。如果没有这些,在进行操作时,会有找不到无从下手的感觉。

3、实战经验:武侠世界中的高手,都是从无数次战斗中取得经验,再武功大进。学习ORACLE也一样,如果没有充分的实验,实际生产环境的实战,仍然只能说是只能入了ORACLE的门,算不上登入大堂。

另外,学习过程中,多做笔记,多思考。做任何事,都需要多思考,学习ORACLE也不例外。对ORACLE的众多的功能和知识点,我们要经常思考,这个功能有什么好处,适用于什么地方,不适用于什么地方,每个知识点之间的联系等等。甚至是要站在超越ORACLE的高度,去思考ORACLE为什么会这样设计。另外,好记性不如烂笔头,除非是天才,很多东西,久了就忘记了。比如前段时间做过CLUSTER表的实验,现在都快忘记了,现赶紧记之(^_^):

对CLUSTER下的表,不能TRUNCATE,只能TRUNCATE CLUSTR,这样TRUNCATE CLUSTR时,将截断CLUSTER下的所有表。

DROP CLUSTER时,如果CLUSTER下有表,则不能DROP。只能将CLUSTER中所有的表DROP后,才能DROP CLUSTER。