yangtingkun
===========================================================
Oracle中的NULL(二)
===========================================================

最近在论坛上经常看到,很多人提出和NULL有关的问题。NULL其实是数据库中特有的类型,Oracle中很多容易出现的错误都是和NULL有关的。

打算简单的总结一下NULL的相关知识。

这一篇介绍NULL布尔运算特点。

Oracle中的NULL(一):http://yangtingkun.itpub.net/post/468/244434


上一篇文章中介绍了Oracle数据库中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

PL/SQL 过程已成功完成。

由于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,所以,查询不会返回记录。

yangtingkun 发表于:2006.12.27 23:03 ::分类: ( ORACLE ) ::阅读:(1111次) :: 评论 (5)
re: Oracle中的NULL(二) [回复]

我觉得null应该这样理解:
1、null本身是一种特殊的数据类型,可以和任何其他类型共同操作,而不存在bool的null或者varchar2的null之分;
2、任何涉及到null的表达式的结果都为null,字符串连接时当空串处理;
3、在bool表达式中,结果null就为false,结合上一条,任何bool表达式中含有null的话结果就为false。

对于上面的in和not in的例子,应该这样解释:

TNAME IN (‘T’, ‘T1’, NULL) TNAME = ‘T’ OR TNAME = ‘T1’ OR TNAME = NULL
IN被转换为三个or连接的子句,尽管第三个子句根据以上的2、3点其结果为null,但是满足第1、2子句的记录还是能被返回。

TNAME NOT IN (‘T’, ‘T1’, NULL) NOT (TNAME = ‘T’ OR TNAME = ‘T1’ OR TNAME = NULL)
NOT IN被转换为一个含有null的bool表达式,其结果始终为null,因此没有记录返回。

SQL> declare
2 a int:=1;
3 b int:=5;
4 begin
5 if (a = 1 or a = 2 or a = null) then
6 dbms_output.put_line('IN => or!');
7 end if;
8
9 if (b != 1 or b != 2 or b != null) then
10 dbms_output.put_line('NOT IN => != or!');
11 end if;
12
13 if not (b = 1 or b = 2 or b = null) then
14 dbms_output.put_line('NOT IN => not or!');
15 end if;
16 end;
17 /

IN => or!
NOT IN => != or!

PL/SQL procedure successfully completed

SQL>

fuyuncat 评论于: 2006.12.28 15:11
re: Oracle中的NULL(二) [回复]

杨兄的文章真是高产!
关于NULL,建议再说说count和index中的null问题smile

fuyuncat 评论于: 2006.12.28 15:15
re: Oracle中的NULL(二) [回复]

还没有连载完,后面打算再写几篇。

yangtingkun 评论于: 2006.12.28 15:54
re: Oracle中的NULL(二) [回复]

看了一下你的意见,也是有道理的,看来有些东西,大家从各个方面理解都是可以的,没有一个定论。

不过上面有一点我并不同意,布尔型中FALSE和NULL还不一样。
具体描述见:http://yangtingkun.itpub.net/post/468/245259

yangtingkun 评论于: 2006.12.28 16:31
re: Oracle中的NULL(二) [回复]

学习了

oraclekumao 评论于: 2007.11.16 11:05

发表评论
标题

在此添加评论
表情符号: smile laughing tongue angry crying sad wassat wink

称呼

邮箱地址(可选)

个人主页(可选)

 authimage


切换风格
新闻聚合
博客日历
文章归档...
最新发表...
最新评论...
最多阅读文章...
最多评论文章...
博客统计...
Blog信息
网站链接...