null介绍 null是数据库中特有的数据类型,当一条记录的某个列为null,则表示这个列的是未知的、是不确定的。既然是未知的,就有无数种的可能性。因此,null并不是一个确定的。 这是null的由来、也是null的基础,所有和null相关的操作的结果都可以从null的概
null值介绍
null是数据库中特有的数据类型,当一条记录的某个列为null,则表示这个列的值是未知的、是不确定的。既然是未知的,就有无数种的可能性。因此,null并不是一个确定的值。
这是null的由来、也是null的基础,所有和null相关的操作的结果都可以从null的概念推导出来。
判断一个字段是否为null,应该用is null或is not null,而不能用‘=’。对null的判断只能定性------(即是不是null(is null/is not null)),而不能定值。简单的说,由于null存在着无数的可能,因此两个null不是相等的关系,同样也不能说两个null就不相等,或者比较两个null的大小,这些操作都是没有意义,得不到一个确切的答案的。因此,对null的=、!=、>、=、 同理,对null进行+、-、*、/等操作的结果也是未知的,所以也是null。
所以,很多时候会这样总结null,除了is null、is not null以外,对null的任何操作的结果还是null。
上面这句话总结的很精辟,而且很好记,所以很多时候人们只记得这句话,而忘了这句话是如何得到的。其实只要清楚null的真正含义,在处理null的时候就不会出错。
说了怎么多,来看一个经典的例子:
sql> create or replace procedure p1 (p_in in number) as
2 begin
3 if p_in >= 0 then
4 dbms_output.put_line('true');
5 else
6 dbms_output.put_line('false');
7 end if;
8 end;
9 /
过程已创建。
sql> create or replace procedure p2 (p_in in number) as
2 begin
3 if p_in 4 dbms_output.put_line('false');
5 else
6 dbms_output.put_line('true');
7 end if;
8 end;
9 /
过程已创建。
上面两个过程是否是等价的?对于熟悉c或java的开发人员来说,可能认为二者是等价的,但是在数据库中,则还要考虑到null的情况。
当输入为null时,可以看到上面两个过程不同的输出:
sql> set serverout on
sql> exec p1(null)
false
pl/sql 过程已成功完成。
sql> exec p2(null)
true
pl/sql 过程已成功完成。
输入为null时,上面两个过程中的判断的结果都是一样的,不管是null >= 0还是null 由于null所具有的特殊性,在处理数据库相关问题时应该对null的情况额外考虑,否则很容易造成错误。
关于null的布尔运算特点
由于引入了null,在处理逻辑过程中一定要考虑null的情况。同样的,数据库中的布尔值的处理,也是需要考虑null的情况,这使得布尔值从原来的true、false两个值变成了true、false和null三个值。
下面是true和false两种情况进行布尔运算的结果:
and操作:
and
true
false
true
true
false
false
false
false
or操作:
or
true
false
true
true
true
false
true
false
上面是熟悉的true和false两个值进行布尔运算的结果,如果加上一个null的情况会怎样?null的布尔运算是否会像null的算术运算那样结果都是null呢?下面通过一个过程来进行说明:
sql> set serverout on size 100000
sql> declare
2 type t_boolean is table of boolean index by binary_integer;
3 v_bool1 t_boolean;
4 v_bool2 t_boolean;
5
6 procedure p(p_in1 boolean, p_in2 boolean, p_operator in varchar2) as
7 v_result boolean;
8 begin
9 if p_in1 is null then
10 dbms_output.put('null ');
11 elsif p_in1 then
12 dbms_output.put('true ');
13 else
14 dbms_output.put('false ');
15 end if;
16
17 if p_operator = 'and' then
18 dbms_output.put('and ');
19 v_result := p_in1 and p_in2;
20 elsif p_operator = 'or' then
21 dbms_output.put('or ');
22 v_result := p_in1 or p_in2;
23 else
24 raise_application_error('-20000', 'input parameter p_operator error');
25 end if;
26
27 if p_in2 is null then
28 dbms_output.put('null');
29 elsif p_in2 then
30 dbms_output.put('true');
31 else
32 dbms_output.put('false');
33 end if;
34
35 if v_result is null then
36 dbms_output.put(':null');
37 elsif v_result then
38 dbms_output.put(':true');
39 else
40 dbms_output.put(':false');
41 end if;
42 dbms_output.new_line;
43 end;
44
45 begin
46 v_bool1(1) := true;
47 v_bool1(2) := false;
48 v_bool1(3) := null;
49 v_bool2 := v_bool1;
50 for i in 1..v_bool1.count loop
51 for j in 1..v_bool2.count loop
52 p(v_bool1(i), v_bool2(j), 'and');
53 p(v_bool1(i), v_bool2(j), 'or');
54 end loop;
55 end loop;
56 end;
57 /
true and true:true
true or true:true
true and false:false
true or false:true
true and null:null
true or null:true
false and true:false
false or true:true
false and false:false
false or false:false
false and null:false
false or null:null
null and true:null
null or true:true
null and false:false
null or false:null
null and null:null
null or null:null
由于null是未知,所以null and null、null or null、null and true和null or false的值都是未知的,这些的结果仍然是null。
那么为什么null and false和null or true得到了一个确定的结果呢?仍然从null的概念来考虑。null是未知的,但是目前null的类型是布尔类型,因此null只有可能是true或者false中的一个。
而根据前面的表格,true and false和false and false的结果都是false,也就是说不管null的值是true还是false,它与false进行and的结果一定是false。
同样的道理,true and true和false and true的结果都是true,所以不管null取何值,null和true的or的结果都是true。
and操作图表变为:
and
true
false
null
true
true
false
null
false
false
false
false
null
null
false
null
or操作图表变为:
or
true
false
null
true
true
true
true
false
true
false
null
null
true
null
null
最后,仍然来看一个例子:
sql> select * from tab;
tname tabtype clusterid
------------------------------ ------- ----------
plan_table table
t table
t1 table
t2 table
t3 table
test table
test1 table
test_corrupt table
t_time table
已选择9行。
sql> select * from tab where tname in ('t', 't1', null);
tname tabtype clusterid
------------------------------ ------- ----------
t table
t1 table
sql> select * from tab where tname not in ('t', 't1', null);
未选定行
.
对于in和not in与null的关系前面并没有说明,不过可以对其进行简单的变形:
tname in (‘t’, ‘t1’, null) tname = ‘t’ or tname = ‘t1’ or tname = null
根据前面的结果,当查询到t或t1这两条记录时,where条件相当于true and false and null,其结果是true,因此返回了两条记录。
tname not in (‘t’, ‘t1’, null) tname != ‘t’ and tname != ‘t1’ and tname != null。
where条件相当于true and true and null,或true and false and null,其最终结果是null或者false,所以,查询不会返回记录。
接着讨论一下null的布尔值运算not。
对于true和false的not运算很简单,not true=false,not false=true,那么如果包含null的情况呢,首先还是用事实来说话
sql> set serverout on size 100000
sql> declare
2 type t_boolean is table of boolean index by binary_integer;
3 v_bool t_boolean;
4
5 procedure p(p_in boolean) as
6 v_result boolean;
7 begin
8 if p_in is null then
9 dbms_output.put('not null');
10 elsif p_in then
11 dbms_output.put('not true');
12 else
13 dbms_output.put('not false');
14 end if;
15
16 v_result := not p_in;
17
18 if v_result is null then
19 dbms_output.put(':null');
20 elsif v_result then
21 dbms_output.put(':true');
22 else
23 dbms_output.put(':false');
24 end if;
25 dbms_output.new_line;
26 end;
27
28 begin
29 v_bool(1) := true;
30 v_bool(2) := false;
31 v_bool(3) := null;
32 for i in 1..v_bool.count loop
33 p(v_bool(i));
34 end loop;
35 end;
36 /
not true:false
not false:true
not null:null
pl/sql 过程已成功完成。
现在我们看到了一个很有趣的结果,not null的结果仍然是null。可能很多人对此并不理解。下面还是从null的基本概念来解释。
null表示的是未知的含义,而增加一个not操作后,并不能使null变为一个确定的值,如果是true,not true将变为false,如果是false,not false将变为true,所有,即使进行了not操作,null本身的不确定性是仍然存在的。这就是最终结果仍然是null的原因。
这里需要注意:这个not null是一个布尔操作,要和sql中的not null约束进行区分。not null约束是一个定性的描述,只是表示列中的数据不允许为null。而这里的布尔操作,却是在进行求值,要得到对null取非的结果,所以仍然得到null。
not true
not false
not null
false
true
null
关于null的字符串表示格式
以前我总说空字符串’’等价于null,但是有些人喜欢钻牛角尖,所以我改一下说法,空字符串’’是null的字符类型的表现格式。
也许有人会认为,null就是null,本身没有类型的一说,但是我认为,null还是有类型的,只不过不同类型的null都用相同的关键字null来表示。而且,null本身也可以转化为任意类型的数据,因此给人的感觉是null没有数据类型。
其实null不但有数据类型,还有默认的数据类型,那就是字符类型。至于这个答案是如何推断出来的,请看:http://blog.csdn.net/mengxiangfeiyang/article/details/7974807
不过上面说的这个默认的数据类型是在极限的情况下测试出来的,如果只是给出一个null,那么它是可以代表任意的类型的。
证明空字符串就是null是很容易的:
sql> select 1 from dual where '' = '';
未选定行
sql> select 1 from dual where '' is null;
1
----------
1
sql> select dump(''), dump(null) from dual;
dump dump
---- ----
null null
上面三个sql语句,任意一个都足以证明空字符串’’就是null。
有些人可能会说,既然’’就是null,为什么不能进行is ’’的判断呢?
sql> select 1 from dual where '' is '';
select 1 from dual where '' is ''
*
第 1 行出现错误:
ora-00908: 缺失 null 关键字
其实从上面的错误信息就可以看到答案。原因就是is null是oracle的语法,在oracle运行的时刻’’是null,但是现在oracle还没有运行这句sql,就由于语法不正确被sql分析器挡住了。oracle的语法并不包含is ’’的写法,所以,这一点并不能称为’’不是null的理由。
那么我为什么还要说’’是null的字符表示形式呢?因为’’和null还确实不完全一样,对于null来说,它表示了各种数据类型的null值。而对于空字符串’’来说,虽然它也具有null的可以任意转化为其他任何数据类型的特点,但是无论是从形式上还是从本质上它都表现出了字符类型的特点。
下面通过一个例子来证明’’本质是字符类型的null。
sql> create or replace package p_test_null as
2 function f_return (p_in in number) return varchar2;
3 function f_return (p_in in varchar2) return varchar2;
4 end;
5 /
程序包已创建。
sql> create or replace package body p_test_null as
2
3 function f_return (p_in in number) return varchar2 as
4 begin
5 return 'number';
6 end;
7
8 function f_return (p_in in varchar2) return varchar2 as
9 begin
10 return 'varchar2';
11 end;
12
13 end;
14 /
程序包体已创建。
sql> select p_test_null.f_return(3) from dual;
p_test_null.f_return(3)
------------------------------------------------------------
number
sql> select p_test_null.f_return('3') from dual;
p_test_null.f_return('3')
------------------------------------------------------------
varchar2
sql> select p_test_null.f_return('') from dual;
p_test_null.f_return('')
------------------------------------------------------------
varchar2
sql> select p_test_null.f_return(null) from dual;
select p_test_null.f_return(null) from dual
*
第 1 行出现错误:
ora-06553: pls-307: 有太多的 'f_return' 声明与此次调用相匹配
从这一点上可以看出’’实际上已经具备了数据类型。所以我将’’表述为空字符串是null的字符类型表现形式。
为什么空字符就是null
试图解释为什么空字符就是null。而且准备简单描述一下字符串合并操作||的特殊性。
根据null的定义,null是不确定、未知的含义,那么为什么字符类型的null是一个空字符呢?而且,对于null的加、减、乘、除等操作的结果都是null,而为什么字符串合并操作||,当输入字符串有一个为空时,不会得到结果null。
sql> select null || 'a', 'b' || null, null || null from dual;
nu ' n
-- - -
a b
上面两个问题需要从null的存储格式上解释。oracle在存储数据时,先是存储这一列的长度,然后存储列数据本身。而对于null,只包含一个ff,没有数据部分。简单的说,oracle用长度ff来表示null。
由于oracle在处理的数据存储的时候尽量避免0的出现,因此,认为这里ff表示的是长度为0也是有一定道理的。或者从另一方面考虑,null只有一个长度,而没有数据部分。
而对于字符串来说,不管是长度为0的字符串还是没有任何数据的字符串,所代表的含义都是一个空字符串。从一点上讲,空字符串就是null也是有一定的道理的。
如果认为空字符串是字符形式的null,那么||操作的结果就不难理解了。
最后需要说明的是,不要将oracle里面的空字符串’’与c里面的空字符串””混淆。c里面的空字符串并非不不含任何数据,里面还包含了一个字符串结束符\0。c语言中的空字符串””对应oracle中ascii表中的0值,既chr(0)。
但chr(0)是一个确定的值,它显然不是null。
sql> select * from dual where chr(0) = chr(0);
d
-
x
sql> select * from dual where chr(0) is null;
未选定行
null和索引的关系
如果说null类型已经比较容易出错了,那么索引问题就让null又一次成为问题的焦点。
大多数人都听说过这样一句话,索引不存储null值。这句话其实比不严谨。如果采用比较严谨的方式来说:b树索引不存储索引列全为空的记录。如果把这句话用在单列索引上,就是前面提到的b树索引不存储null。
首先索引分为btree和bitmap两种,对于btree索引,是不存储null值的,而对于bitmap索引,是存储null值的。
而从索引列的个数来划分,索引非为单列索引和复合索引,对于单列索引来说很简单,如果一条记录中这个索引字段为空,那么索引不会保存这条记录的信息。但是对于复合索引,由于存在着多个列,如果某一个索引列不为空,那么索引就会包括这条记录,即使其他所有的所有列都是null值。
sql> create table t as select * from dba_objects;
表已创建。
sql> desc t
名称 是否为空? 类型
-------------------------------------------- -------- ------------------
owner varchar2(30)
object_name varchar2(128)
subobject_name varchar2(30)
object_id number
data_object_id number
object_type varchar2(19)
created date
last_ddl_time date
timestamp varchar2(19)
status varchar2(7)
temporary varchar2(1)
generated varchar2(1)
secondary varchar2(1)
sql> create index ind_t_object_id on t (object_id);
索引已创建。
sql> exec dbms_stats.gather_table_stats(user, 't', cascade => true)
pl/sql 过程已成功完成。
sql> set autot on exp
sql> select count(*) from t;
count(*)
----------
50297
执行计划
----------------------------------------------------------
plan hash value: 2966233522
-------------------------------------------------------------------
| id | operation | name | rows | cost (%cpu)| time |
-------------------------------------------------------------------
| 0 | select statement | | 1 | 41 (3)| 00:00:01 |
| 1 | sort aggregate | | 1 | | |
| 2 | table access full| t | 50297 | 41 (3)| 00:00:01 |
-------------------------------------------------------------------
sql> select /*+ index(t ind_t_object_id) */ count(*) from t;
count(*)
----------
50297
执行计划
----------------------------------------------------------
plan hash value: 2966233522
-------------------------------------------------------------------
| id | operation | name | rows | cost (%cpu)| time |
-------------------------------------------------------------------
| 0 | select statement | | 1 | 41 (3)| 00:00:01 |
| 1 | sort aggregate | | 1 | | |
| 2 | table access full| t | 50297 | 41 (3)| 00:00:01 |
-------------------------------------------------------------------
oracle的优化器在确定是否使用索引的时候,第一标准是能否得到一个正确的结果。由于object_id是可以为空的,而索引列不包含为空的记录。因此通过索引扫描无法得到一个正确的结果,这就是select count(*) from t不会使用object_id上的索引的原因。
而对于bitmap索引,则是另外的情况:
sql> drop index ind_t_object_id;
索引已删除。
sql> create bitmap index ind_b_t_data_id on t (data_object_id);
索引已创建。
sql> select count(*) from t;
count(*)
----------
50297
执行计划
----------------------------------------------------------
plan hash value: 3051411170
-------------------------------------------------------------------------
| id | operation | name | rows | cost (%cpu)|
-------------------------------------------------------------------------
| 0 | select statement | | 1 | 2 (0)|
| 1 | sort aggregate | | 1 | |
| 2 | bitmap conversion count| | 50297 | 2 (0)|
| 3 | bitmap index full scan| ind_b_t_data_id | | |
-------------------------------------------------------------------------
sql> select count(*) from t where data_object_id is null;
count(*)
----------
46452
执行计划
----------------------------------------------------------
plan hash value: 2587852253
-----------------------------------------------------------------------------
| id | operation | name | rows | bytes | cost (%cpu)|
-----------------------------------------------------------------------------
| 0 | select statement | | 1| 2| 2 (0)|
| 1 | sort aggregate | | 1| 2| |
| 2 | bitmap conversion count | | 46452| 92904| 2 (0)|
|* 3 | bitmap index single value| ind_b_t_data_id| | | |
------------------------------------------------------------------------------
predicate information (identified by operation id):
---------------------------------------------------
3 - access(data_object_id is null)
从上面的结果不难看出bitmap索引中是包含null的。
下面看看复合索引的情况:
sql> drop index ind_b_t_data_id;
索引已删除。
sql> create index ind_t_object_data on t(object_id, data_object_id);
索引已创建。
sql> exec dbms_stats.gather_table_stats(user, 't', method_opt => 'for all indexed columns')
pl/sql 过程已成功完成。
sql> select object_id, data_object_id from t where object_id = 135;
object_id data_object_id
---------- --------------
135
执行计划
----------------------------------------------------------
plan hash value: 1726226519
---------------------------------------------------------------------------
| id | operation | name | rows | bytes | cost (%cpu)|
---------------------------------------------------------------------------
| 0 | select statement | | 1 | 7 | 1 (0)|
|* 1 | index range scan| ind_t_object_data | 1 | 7 | 1 (0)|
---------------------------------------------------------------------------
predicate information (identified by operation id):
---------------------------------------------------
1 - access(object_id=135)
虽然结果中包含了null值,但是oracle并没有读取表,而仅仅通过索引扫描就返回了结果,这说明复合索引中是可能包含null值的。
本文简单说明了索引和null值的关系。这里并没有对反键索引(reverse)、逆序索引(desc)、函数索引(fbi)和cluster索引进行说明。
原因是这些索引其实都属于离不开btree索引和bitmap索引的范畴。不必关心索引是否倒序或反键,只要是btree索引,就不会存储全null记录,反之,只要是bitmap索引就会存储null值。
唯一需要注意的是函数索引,函数索引的真正索引列是函数的计算结果而不是行记录中的数据,清楚了这一点函数索引其实和普通索引就没有什么区别了。
最后说明一下域索引。由于域索引的实现本身可能会很复杂,oracle可能在内部是用一套表和过程来实现的,因此对于域索引是否存储null,要根据域索引的实现去进行具体的分析了。
null对sql使用索引的影响
观点一:判断一个列is not null不会使用索引。
其实这个观点从一般意义上也解释不同,因为b树索引本身不存储键值全为null的记录,所以通过索引扫描得到的结果一定满足is not null的要求。
sql> create table t as select * from dba_objects;
表已创建。
sql> create index ind_t_dataid on t(data_object_id);
索引已创建。
sql> exec dbms_stats.gather_table_stats(user, 't')
pl/sql 过程已成功完成。
sql> set autot trace
sql> select count(*) from t where data_object_id is not null;
execution plan
----------------------------------------------------------
0 select statement optimizer=choose (cost=2 card=1 bytes=2)
1 0 sort (aggregate)
2 1 index (full scan) of 'ind_t_dataid' (non-unique) (cost=26 card=2946 bytes=5892)
statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
5 consistent gets
4 physical reads
0 redo size
377 bytes sent via sql*net to client
503 bytes received via sql*net from client
2 sql*net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
由于索引的存储特性和is not null访问本身没有冲突,因此,这种情况下很容易通过索引来得到相应的结果。
观点二:判断一个列is null不会使用索引。
这里不讨论bitmap索引。由于bitmap索引保存null值,所以讨论bitmap索引没有意义。这里仅讨论b树索引。
sql> alter table t modify owner not null;
表已更改。
sql> update t set object_id = null where rownum = 1;
已更新 1 行。
sql> create index ind_t_object_owner on t (object_id, owner);
索引已创建。
sql> exec dbms_stats.gather_table_stats(user, 't', method_opt => 'for all indexed columns size 200')
pl/sql 过程已成功完成。
sql> set autot trace
sql> select * from t where object_id is null;
execution plan
----------------------------------------------------------
0 select statement optimizer=choose (cost=3 card=1 bytes=93)
1 0 table access (by index rowid) of 't' (cost=3 card=1 bytes=93)
2 1 index (range scan) of 'ind_t_object_owner' (non-unique) (cost=2 card=1)
statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
1156 bytes sent via sql*net to client
503 bytes received via sql*net from client
2 sql*net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
在sql和plsql中一些处理null的一些问题
null的最大的特点就是两个null是不相等的。如果用等号来判断两个null是否相等得到的结果一定是null。从唯一约束的特点也可以看到,对于建立了唯一约束的列,oracle允许插入多个null值,这时因为oracle不认为这些null是相等的。
sql> create table t (id number, constraint un_t unique(id));
表已创建。
sql> insert into t values (1);
已创建 1 行。
sql> insert into t values (1);
insert into t values (1)
*
error 位于第 1 行:
ora-00001: 违反唯一约束条件 (yangtk.un_t)
sql> insert into t values (null);
已创建 1 行。
sql> insert into t values (null);
已创建 1 行。
但是有的时候,oracle会认为null是相同的,比如在group by和distinct操作中。这个时候,oracle会认为所有的null都是一类的。
还有一种情况,就是在decode函数中。如果表达式为decode(col, null, 0, 1),那么如果col的值为null,oracle会认为这种情况与第二个参数的null值相匹配,会返回0。不过这里只是给人感觉null值是相等的,oracle在实现decode函数的时候,仍然是通过is null的方式进行的判断。
对于大多数的常用函数来说,如果输入为null,则输出也是null。nvl、nvl2、decode和||操作是个例外。他们在输入参数为null的时候,结果可能不是null。不过归结其原因是因为,这些函数都有多个参数,当多个参数不全为null时,结果可能不是null,如果输入参数均为null,那么得到的输出结果也是null。
null还有一个特点,就是一般聚集函数不会处理null值。不管是max、min、avg还是sum,这些聚集函数都不会处理null。注意这里说的不会处理null,是指聚集函数会直接忽略null值记录的存在。除非是聚集函数处理的列中包含的全部记录都是null,这种情况下,上面这些聚集函数会返回null值。
sql> delete t where id = 1;
已删除 1 行。
sql> select nvl(to_char(id), 'null') from t;
nvl(to_char(id),'null')
----------------------------------------
null
null
sql> select max(id) from t;
max(id)
----------
sql> select avg(id) from t;
avg(id)
----------
sql> insert into t values (1);
已创建 1 行。
聚集函数中比较特殊的是count,第一个特殊点是count不会返回null值,即使表中没有记录,或者count(col)中,col列的记录全为null,count也会返回0值而不是null。第二个特殊点就是count(*)或count(常量)的形式。这种形式使得count可以计算包含null记录在内的记录总数。
sql> select count(*), count(1), count('a'), count(id), count(null) from t;
count(*) count(1) count('a') count(id) count(null)
---------- ---------- ---------- ---------- -----------
3 3 3 1 0
最后简单说一下avg,avg(col)等价于sum(col)/count(col),不等价于sum(col)/count(*):
sql> select avg(id), sum(id)/count(id), sum(id)/count(*) from t;
avg(id) sum(id)/count(id) sum(id)/count(*)
---------- ----------------- ----------------
1 1 .333333333
check约束中的null条件
sql> create table t
2 (
3 id number,
4 flag char(1) not null,
5 constraint ck_flag check (flag in ('a'))
6 );
表已创建。
sql> insert into t (id, flag) values (1, 'a');
已创建 1 行。
sql> insert into t (id, flag) values (1, 'b');
insert into t (id, flag) values (1, 'b')
*
第 1 行出现错误:
ora-02290: 违反检查约束条件 (yangtk.ck_flag)
sql> insert into t (id, flag) values (1, null);
insert into t (id, flag) values (1, null)
*
第 1 行出现错误:
ora-01400: 无法将 null 插入 (yangtk.t.flag)
由于flag列上的check约束和not null约束,会导致除了’a’以外的数据都无法插入。但是如果在约束上添加一个null值:
sql> alter table t drop constraint ck_flag;
表已更改。
sql> alter table t add constraint ck_flag check (flag in ('a', ''));
表已更改。
oracle不会检查同一个列上的多个check约束是否会发生冲突,因此上面添加flag in null这个约束并不会报错。
但是这种添加的null,会导致这个check约束失效:
sql> insert into t (id, flag) values (1, 'a');
已创建 1 行。
sql> insert into t (id, flag) values (1, 'b');
已创建 1 行。
sql> insert into t (id, flag) values (1, 'c');
已创建 1 行。
sql> insert into t (id, flag) values (1, null);
insert into t (id, flag) values (1, null)
*
第 1 行出现错误:
ora-01400: 无法将 null 插入 (yangtk.t.flag)
现在check约束ck_flag已经完全失去作用,对于flag列除了null值外,其他任何值都可以插入。
check的约束保证插入的数据不会导致check检查的结果为假,check结果为真和为空的记录都可以插入。
对于flag in (‘a’, ‘’)的约束而言,任何记录的check结果不是true就是null。因此任何记录都是可以插入的