欢迎投稿

今日深度:

从NOTNULL完整性约束的介绍看Concept的强大

从NOTNULL完整性约束的介绍看Concept的强大


Oracle Concept是我们平日学习的官方宝典,之所以称为宝典,一个原因就是很多问题,归根结底可能都可以从Concept中找到一些说明和出处,记得有一次Kamus回答网友问题的时候(具体问题忘了),援引的就是Concept某一章节的一句话。另一个原因就是其内容表述言简意赅,一些使用上的细节用简单的几个单词或几句话就带出来了,但往往却包含着丰富的含义,有点于无声处听惊雷的感觉。

例如第五章介绍NOT NULL Integrity Constraints的时候,有一句:

You can only add a column with a NOT NULL constraint if the table does not contain any rows or if you specify a default value.

翻译过来就是,只有当表中无数据或者包含默认值的情况下,可以为列增加一个NOT NULL约束。简单的一句话,其实蕴含着不少信息,我们用实验来说明。

环境准备:11.2.0.4

SQL> create table tbl_null(
2 id number primary key,
3 name varchar2(5)
4 );
Table created.

SQL> insert into tbl_null values(1, 'a');
1 row created.

SQL> insert into tbl_null values(2, '');
1 row created.

SQL> select * from tbl_null;
ID NAME
---------- -----
1 a
2

这里创建了一张测试表TBL_NULL,其中包含一个name字段,VARCHAR2类型,允许为空,有两条记录,其中一条name为空。

实验1:验证“当表中无数据”时才可以增加NOT NULL约束。
此时表中有一条name是空的记录,如果执行增加非空约束的操作:

SQL> alter table tbl_null modify name not null;
alter table tbl_null modify name not null
*
ERROR at line 1:
ORA-02296: cannot enable (BISAL.) - null values found

由于name字段有一条记录是空值,因此就会提示发现null值,禁止执行。
删除数据后执行:

SQL> truncate table tbl_null;
Table altered.

SQL> alter table tbl_null modify name not null;
Table altered.

表中无数据时可以随意增加NOT NULL约束。

实验2:验证“包含默认值”的情况下,可以新增NOT NULL约束。

SQL> select * from tbl_null;
    ID NAME
---------- -----
     1 a

SQL> alter table tbl_null modify name not null;
Table altered.

当表中数据name字段都有值的情况下,可以新增NOT NULL约束。很好理解,如果字段值当前为空,又要设置NOT NULL约束,Oracle不知道如何设置这些空值。

实验3:表中记录有空值,还要增加NOT NULL约束。
那么问题来了,如果表中记录有空值,还要增加NOT NULL约束,但又不能按照实验1先清空数据做,也不能按照实验2都赋予一个默认值来做,怎么办?
此时可以使用enable novalidate选项,让其增加NOT NULL约束时不对已存在数据进行非空检查。

SQL> select * from tbl_null;
ID NAME
---------- -----
1 a
2

SQL> alter table tbl_null modify name not null enable novalidate;
Table altered.

SQL> insert into tbl_null values(3, '');
insert into tbl_null values(3, '')
*
ERROR at line 1:
ORA-01400: cannot insert NULL into ("BISAL"."TBL_NULL"."NAME")

SQL> select * from tbl_null;
ID NAME
---------- -----
1 a
2

可以看出,新增空值的操作报错,提示不允许插入空值,但实际上表中存在name为空的记录。即使用enable novalidate选项时,Oracle不会校验历史数据是否符合约束,只会对新增数据进行校验。
官方对于enable novalidate的解释:

The database checks the constraint, but it need not be true for all rows. Thus, existing rows can violate the constraint, but new or modified rows must conform to the rules.

?如果应用上对于字段NOT NULL的约束有特殊处理,可以在此时UPDATE旧数据的空值为默认值,保证表中该字段值都有值,即在表中字段有空值的情况下新增NOT NULL约束。

实验4:使用和不使用enable novalidate有什么区别?
进一步,我们再看看使用和不使用enable novalidate后台的区别。

1.创建测试表,模拟5000000行记录:

SQL> declare 
  2    n number;
  3    j number;
  4  begin
  5    for i in 1 .. 5000000 loop
  6      insert into tbl_null values(i, 'A');
  7      n := mod(i, 100000);
  8      if (n = 0) then
  9        j := i/100000;
 10        dbms_output.put_line('the '||j||' insert');
 11        commit;
 12      end if;
 13    end loop;
 14    commit;
 15  end;
 16  /
PL/SQL procedure successfully completed.

SQL> select count(*) from tbl_null;
  COUNT(*)
----------
   5000000

2.分别使用和不使用enable novalidate进行测试:

SQL> alter table tbl_null modify name not null;
Table altered.
Elapse: 00:00:00.17

SQL> alter table tbl_null_0 modify name not null enable novalidate;
Table altered.
Elapsed: 00:00:00.05

粗略地看,使用enable novalidate的方法会比不使用要略快一些。

3.从10046的trace文件看出,使用和不使用enable novalidate,首先都会执行以下SQL:

LOCK TABLE "TBL_NULL" IN EXCLUSIVE MODE  NOWAIT

以NOWAIT方式对TBL_NULL表加上排他锁。但不使用enable novalidate则会有以下和TBL_NULL相关的两步额外操作:

select /*+ all_rows ordered */ A.rowid, :1, :2, :3 
from
 "BISAL"."TBL_NULL" A where( "NAME" is null)
SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS IGNORE_WHERE_CLAUSE 
  NO_PARALLEL(SAMPLESUB) opt_param('parallel_execution_enabled', 'false') 
  NO_PARALLEL_INDEX(SAMPLESUB) NO_SQL_TUNE */ NVL(SUM(C1),0), NVL(SUM(C2),0) 
FROM
 (SELECT /*+ IGNORE_WHERE_CLAUSE NO_PARALLEL("A") FULL("A") 
  NO_PARALLEL_INDEX("A") */ 1 AS C1, CASE WHEN "A"."NAME" IS NULL THEN 1 ELSE 
  0 END AS C2 FROM "BISAL"."TBL_NULL" "A") SAMPLESUB

从这两句SQL可以看出他要检索所有name值是空的记录,且使用的都是全表扫描。虽然我还不是很清楚这两步操作的真正意义,但一定程度上可以说明使用enable novalidate快的一些可能原因。

总结
增加NOT NULL约束的操作,看似非常简单,官方文档中也是轻描淡写地几句话就介绍完了,但实际上我们还是可以用实验测试每个条件,深刻体会其背后的一些原因。而且Oracle很多知识点之间是互相关联的,eygle曾经说过“由点及面”地学习Oracle,我想这就是这个道理了。
当然,让我体会更深的是,自己还是有太多要学习的知识,作为一个Oracle初学者,我仍在路上。。。共勉。

www.htsjk.Com true http://www.htsjk.com/oracle/23977.html NewsArticle 从NOTNULL完整性约束的介绍看Concept的强大 Oracle Concept是我们平日学习的官方宝典,之所以称为宝典,一个原因就是很多问题,归根结底可能都可以从Concept中找到一些说明和出处,记得有...
评论暂时关闭