ORACLE关于NULL的总结

NULL的官方定义
The PL/SQL data type BOOLEAN stores logical values, which are the Boolean values TRUE and FALSE and the value NULL. NULL represents an unknown value .
NULL值是关系数据库系统布尔型(TRUE、FALSE、NULL)中比较特殊类型的一种值,NULL代表未知的。


NULL的小结:
1、 NULL的判断只能定性,而不能定值,即 NULL不可用于比较大小,所有与NULL的操作结果都是NULL
备注:简单的说,由于NULL存在着无数的可能,因此两个NULL值既不是相等的关系,又不是不相等的关系,不能比较两个NULL的大小,因此,对 NULL 的=、!=、>、<、>=、<=、+ 、-、*、/等操作的结果都是未知的,所以这些操作的结果仍然是NULL

2、 NULL与ORDER BY
对字段进行排序时,所有的 NULL都默认放在最后

3、 NULL与COUNT()
count(column)不包括对NULL的统计 ,count(*)包括对NULL的统计是因为oracle有一个伪列rowid不会为NULL
诀窍1、如果要查询一个表的记录数,可以通过全表扫描的方法,也可以通过COUNT非空列记录数的方法,如果此时非空列上存在索引,就可以直接访问索引获得数据 
诀窍2、如果要查询一个可能为空字段的非空记录数,如果该列上建立了单列索引,直接访问索引可以获得数据

4、 索引不存储NULL值的记录
索引是有序排列的,当一个空值进入索引时,因无法确定其在索引中的位置,所以oracle的索引不存储索引列全为NULL值的记录,扫描索引会漏掉索引列全为NULL值的记录, oracle永远把确保目标SQL结果的正确性放在第一位,遇到查询NULL值的情况就只能走全表扫描
备注1:单列索引时,如果一条记录中这个字段为NULL,那么索引不会保存这条记录
备注2:复合索引时,如果一条记录中组成复合索引的所有字段都是null时,那么索引不会保存这条记录(如果某一个索引列不为空,那么索引就会包括这条记录,即使其他所有的 列都是NULL值)

5、 NULL与唯一性索引的关系
5.1、单列具有唯一性索引时,该列可以插入多行null,因为Oracle不认为这些NULL是相等的
5.2、多列具有复合唯一性索引时,多列不能插入多行null和非null一样组合的数据(比如两列A,B,不可以插入多行1,null,但可以插入多行null,null)

6、 NULL与主键的关系
主键=NOT NULL + UNIQUE CONSTRAINT + UNIQUE INDEX
某列创建为主键时, 原来该列的字段属性可以为NULL ,如果该列已经存在NULL值,主键无法创建,如果该列不存在NULL值,主键创建成功后,该列的字段属性自动变成NOT NULL

7、 NULL与新增not null字段的理解
7.1、alter table t add col1(number)
不需要更新字段值,执行时间比较短。
像上面增加列的操作时,会同时更新sys.ecol$和sys.col$数据字典表,若以后再修改这个默认值,则只是会修改sys.col$的值,且以后每次查询也是从sys.col$的default$列获取 默认值,我们可以根据sys.eclo$、sys.col$和dba_objects查询相关表和字段信息,
7.2、alter table t add col2(number) default 0;
需要更新字段值,执行时间比较长。
如果新增一个含有默认值的字段,那么会立即更新每一行,在更新过程中,那么执行时间会很久,取决于表中数据量的大小,会有一个EXCLUSIVE级别的锁在该表上,期间会影响所 有记录的DML操作,可能会因UNDO不足对其他操作有影响。。
7.3、alter table t add col3(number) default 0 not null;
不需要更新字段值,执行时间比较短。
默认值是存储于数据字典表中的,并不是保存在原表记录上,即新增一个NOT NULL和默认值的字段,以后每次需要使用该字段时,默认值都是从数据字典中查询到的,这样就减少 了新增字段时的DDL语句时间,也减少了存储空间(不用每条需要使用默认值的记录都存储默认值)。
所以:大表新增有默认值的字段要特别小心,11g要使用 d efault XX not null
11g新特性,新增一个有默认值的NOT NULL约束的字段,默认值不会像以前一样,插入每条记录中,而是会存储于数据字典表
12C又更进一步了,即新增有默认值且允许为null时,也不会更新每一行

8、 NULL与索引\全表扫描的关系(IS NULL、IS NOT NULL的条件)
8.1、列是NULL属性,且该列只有单列索引, 直接select该列 没有任何where条件走全表扫描,即使hint中指定了走索引

select object_id from table1 --object_id允许为null,建有索引,一定全表扫描

select * from table1 where object_id=XX --object_id允许为null,建有索引,会走索引,因为XX不是null

8.1的解决之道: 选择合适的非空字段创建组合索引 ,即组合列的组合数据 不全为NULL会存放在索引中 ,这样就直接查询索引了
8.2、列只有单列索引,列 不管是否NULL属性 ,where条件引用该列采用 IS NULL则走全表扫描 ,不走索引,即使hint中指定了走索引
8.2的解决之道: IS NULL希望使用索引的情况,则需要结合查询条件选择合适的非空字段创建组合索引 ,即组合列的组合数据 不全为NULL会存放在索引中 ,这样就会走索引扫描了
8.3、IS NOT NULL走索引的条件和普通走索引条件一样,都是字段内容大部分都是NULL,只有少部分是非NULL时
8.3的理解:如果在索引列上条件为IS NOT NULL,因为索引列的所有非空值都存储在索引中,按道理也是可以走索引的。但是如果走索引,优化程序需要从索引中读取每一个索引 键值,再映射到表中的行。如果字段小部分内容是NULL,大部分都是非NULL,那就是字段的大部分数据都在索引上,大部分数据从索引获取键值再映射到表上,那还不如直接全表 扫描呢

9、 向后引申:where条件走索引的前提
9.1、where条件囊括的数据键值在索引上能够找到
9.2、where条件囊括的物理数据块占整表物理数据块范围比较小,也就是通过索引扫描比全表扫描快(为什么是物理数据块,而非数据行数,具体见CLUSTERING_FACTOR)

10 、官方文档可以佐证索引不存储空块的一段话如下
Fast full index scans are an alternative to a full table scan when the index contains all the columns that are needed for the query, and at least one column in the index key has the NOT NULL constraint

11 、子查询有null值时,not in (子查询)的结果是null,而in  (子查询 ) 的结果不会这样
SELECT distinct emp.employee_id FROM employees emp order by 1;--107行,没有null的行
SELECT distinct mgr.manager_id FROM employees mgr order by 1;--19行,最后一行是null
SELECT emp.employee_id,emp.last_name FROM employees emp WHERE emp.employee_id not IN (SELECT mgr.manager_id FROM employees mgr);--0行结果
SELECT emp.employee_id,emp.last_name FROM employees emp WHERE emp.employee_id not IN (SELECT mgr.manager_id FROM employees mgr where mgr.manager_id is not null);--89行结果
SELECT emp.employee_id,emp.last_name FROM employees emp WHERE emp.employee_id IN (SELECT mgr.manager_id FROM employees mgr);--18行结果
SELECT emp.employee_id,emp.last_name FROM employees emp WHERE emp.employee_id IN (SELECT mgr.manager_id FROM employees mgr where mgr.manager_id is not null);--18行结果


12 、where条件的字段如果值有null,如果where后面还有not in子查询,则会忽略外查询中null的查询结果
select * from employees where DEPARTMENT_ID not in (select DEPARTMENT_ID from departments);--没有结果
select * from employees where nvl(DEPARTMENT_ID,1) not in (select DEPARTMENT_ID from departments);--有DEPARTMENT_ID为null的那一行
select * from employees where DEPARTMENT_ID  in (select DEPARTMENT_ID from departments);--106行记录
select * from employees where nvl(DEPARTMENT_ID,1)  in (select DEPARTMENT_ID from departments)--106行记录

select department_id from departments order by 1;--27行,没有null
SELECT department_id FROM employees order by 1;--107行,最后一行是null
SELECT employee_id, last_name FROM employees e,departments d where e.department_id=d.department_id ORDER BY d.department_name;--结果为106行,employee_id=178这行的department_id字段是null值,不能用于=比较
SELECT employee_id, last_name FROM employees e ORDER BY (SELECT department_name FROM departments d WHERE e.department_id = d.department_id);--结果为107行,employee_id=178这行的department_id字段是null值,所以排最后







-- NULL不可用于比较
SQL> select count(*) from test1;
  COUNT(*)
----------
     25251
SQL> insert into TEST1(OWNER,OBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID) values('test','test',null,1);
1 row created.
SQL> insert into TEST1(OWNER,OBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID) values('test','test',null,2);
1 row created.
SQL> insert into TEST1(OWNER,OBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID) values('test','test',null,3);
1 row created.
SQL> select * from test1 where OBJECT_ID>null;
no rows selected
SQL> select * from test1 where OBJECT_ID<null;
no rows selected
SQL> select * from test1 where OBJECT_ID=null;
no rows selected
SQL> select count(*) from test1 where OBJECT_ID is null;
  COUNT(*)
----------
         3

-- NULL值相关的操作同样都为NULL
SQL> select OBJECT_ID from test1 where OBJECT_ID=200;
 OBJECT_ID
----------
       200
SQL> select OBJECT_ID+null from test1 where OBJECT_ID=200;
OBJECT_ID+NULL
--------------


-- NULL与ORDER BY
SQL> select * from hid;
      HIDD HNAME
---------- --------------------
         1
           2
         3
           1
         2
SQL> select * from hid order by 1;
      HIDD HNAME
---------- --------------------
         1
         2
         3
           1
           2
SQL> select * from hid order by 2;
      HIDD HNAME
---------- --------------------
           1
           2
         2
         1
         3
SQL>

-- NULL与COUNT()
SQL> select * from test_null;
       HID HNAME
---------- --------------------
         1 1
         2 2

         3
SQL> select count(*) from test_null;
  COUNT(*)
----------
         4
SQL> select count(hname) from test_null;
COUNT(HNAME)
------------
           2
SQL> select count(hid) from test_null;
COUNT(HID)
----------
         3
SQL> select count(rowid) from test_null;
COUNT(ROWID)
------------
           4

-- NULL与唯一性索引的关系
SQL> create unique index ind_unique on hid(hidd,hname);
Index created.
SQL> insert into hid values(9,null);
1 row created.
SQL> insert into hid values(9,null);
insert into hid values(9,null)
*
ERROR at line 1:
ORA-00001: unique constraint (SYS.IND_UNIQUE) violated
SQL> insert into hid values(null,null);
1 row created.
SQL> insert into hid values(null,null);
1 row created.


-- NULL与索引\全表扫描的关系(IS NULL、IS NOT NULL的条件)
</null;
SQL> desc TEST1
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 OWNER                                              VARCHAR2(30)
 OBJECT_NAME                                        VARCHAR2(128)
 OBJECT_ID                                          NUMBER
 DATA_OBJECT_ID                             NOT NULL NUMBER
 OBJECT_TYPE                                        VARCHAR2(19)
 STATUS                                             VARCHAR2(7)
 TEMPORARY                                          VARCHAR2(1)
 GENERATED                                          VARCHAR2(1)
 SECONDARY                                          VARCHAR2(1)
 NAMESPACE                                          NUMBER
SQL> create index ind_O_ID on TEST1( OBJECT_ID );
SQL> create index ind_D_O_ID on TEST1( DATA_OBJECT_ID );
SQL> select count(*) from TEST1;
  COUNT(*)
----------
     25248
SQL> select count(*) from TEST1 where DATA_OBJECT_ID=OBJECT_ID ;
  COUNT(*)
----------
     25248
SQL> select * from test1 where OBJECT_ID is null;
no rows selected
SQL> set autotrace traceonly exp
SQL> set linesize 200
--- 直接查询索引字段, 如果字段属性为null,则走全表扫描
SQL> select OBJECT_ID from test1;
Execution Plan
----------------------------------------------------------
Plan hash value: 4122059633
---------------------------------------------------------------------------
| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |       | 28775 |   365K|    60   (2)| 00:00:01 |
|   1 |   TABLE ACCESS FULL | TEST1 | 28775 |   365K|    60   (2)| 00:00:01 |
---------------------------------------------------------------------------

SQL> select DATA_OBJECT_ID from test1;
Execution Plan
----------------------------------------------------------
Plan hash value: 3894717762
-----------------------------------------------------------------------------------
| Id  | Operation            | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |            | 28775 |   365K|    17   (0)| 00:00:01 |
|   1 |   INDEX FAST FULL SCAN | IND_D_O_ID | 28775 |   365K|    17   (0)| 00:00:01 |
-----------------------------------------------------------------------------------


-- where条件是索引字段,如果条件值不为null,则走索引
SQL> select * from test1 where object_id=99 ;
Execution Plan
----------------------------------------------------------
Plan hash value: 321031222
----------------------------------------------------------------------------------------
| Id  | Operation                   | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          |     1 |   144 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TEST1    |     1 |   144 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IND_O_ID |     1 |       |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------------------

SQL> select * from test1 where data_object_id=99;
Execution Plan
----------------------------------------------------------
Plan hash value: 3689194030
------------------------------------------------------------------------------------------
| Id  | Operation                   | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |            |     1 |   144 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TEST1      |     1 |   144 |     2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IND_D_O_ID |     1 |       |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------------------


-- where条件是索引字段,字段只有单列索引,如果条件为is null,必走全表扫描
SQL> select * from test1 where object_id is null ;
Execution Plan
----------------------------------------------------------
Plan hash value: 4122059633
---------------------------------------------------------------------------
| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |       |     2 |   288 |    60   (2)| 00:00:01 |
|*  1 |   TABLE ACCESS FULL | TEST1 |     2 |   288 |    60   (2)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("OBJECT_ID" IS NULL)

--where字段是not null属性, 条件为is null,也走全表扫描?但是null和not null字段的复合索引就会走索引?
原因是因为not null时,索引字段永远不存在null值,但是复合索引会出现一个索引建值一个字段可能是null另一个字段不是,即索引键值会有null

SQL> select * from test1 where data_object_id is null;
Execution Plan
----------------------------------------------------------
Plan hash value: 1588389598
----------------------------------------------------------------------------
| Id  | Operation          | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |       |     1 |   144 |     0   (0)|          |
|*  1 |  FILTER            |       |       |       |            |          |
|   2 |   TABLE ACCESS FULL| TEST1 | 28775 |  4046K|    60   (2)| 00:00:01 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter( NULL IS NOT NULL )



--- where条件是索引字段,如果条件为is not null,为什么没走索引?
SQL> select * from test1 where OBJECT_ID is not null ;
Execution Plan
----------------------------------------------------------
Plan hash value: 4122059633
---------------------------------------------------------------------------
| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |       | 28775 |  4046K|    60   (2)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TEST1 | 28775 |  4046K|    60   (2)| 00:00:01 |
---------------------------------------------------------------------------

SQL> select * from test1 where DATA_OBJECT_ID is not null;
Execution Plan
----------------------------------------------------------
Plan hash value: 4122059633
---------------------------------------------------------------------------
| Id  | Operation         | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |       | 28775 |  4046K|    60   (2)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| TEST1 | 28775 |  4046K|    60   (2)| 00:00:01 |
---------------------------------------------------------------------------
SQL>
解释: 如果在索引列上条件为Is Not Null ,因为索引列的所有非空值都存储在索引中,按道理也是可以走索引的 。但是本例中count是 25248, DATA_OBJECT_ID=OBJECT_ID的count也是25248,因为 DATA_OBJECT_ID是NOT NULL的, 也就是说结果为NULL的count是0, 也就是 IS NOT  NULL 数据就是全表数据 如果走索引,优化程序需要从索引中读取每一个索引键值 ,再映射到表中的行。整表的每一行都从索引获取键值再映射到表上,那还不如直接全表扫描


-- is not null是会走索引的
SQL> create table test2 as select * from test1;
Table created.
SQL> update test2 set OWNER=null;
25251 rows updated.
SQL> select count(*) from test2 where OWNER is not null;
  COUNT(*)
----------
         0
SQL> select count(*) from test2 where OWNER is null;
  COUNT(*)
----------
     25251
SQL> update test2 set OWNER='good' where rownum<50;
49 rows updated.
SQL> create index idx_owner2 on test2(owner);
Index created.
SQL> exec dbms_stats.gather_table_stats('SYS','TEST2',cascade=>true);
PL/SQL procedure successfully completed.
SQL> set autotrace traceonly exp
SQL> select * from TEST2 where owner is not null;
Execution Plan
----------------------------------------------------------
Plan hash value: 411567793
------------------------------------------------------------------------------------------
| Id  | Operation                   | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |            |    49 |  2548 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TEST2      |    49 |  2548 |     2   (0)| 00:00:01 |
|*  2 |   INDEX FULL SCAN           | IDX_OWNER2 |    49 |       |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("OWNER" IS NOT NULL)


-- 建立复合索引后is null走索引的, 直接查询该索引字段也走索引
SQL> create index ind_fuhe on test1(OBJECT_ID,DATA_OBJECT_ID);
Index created.
SQL> set linesize 200
SQL> set autotrace traceonly exp
SQL> select * from test1 where object_id is null;
Execution Plan
----------------------------------------------------------
Plan hash value: 2734972870
----------------------------------------------------------------------------------------
| Id  | Operation                   | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          |     3 |   168 |     3   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| TEST1    |     3 |   168 |     3   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | IND_FUHE |     3 |       |     2   (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("OBJECT_ID" IS NULL)

SQL> select object_id from test1;
Execution Plan
----------------------------------------------------------
Plan hash value: 3653525288
---------------------------------------------------------------------------------
| Id  | Operation            | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |          | 25251 |   123K|    22   (0)| 00:00:01 |
|   1 |   INDEX FAST FULL SCAN | IND_FUHE | 25251 |   123K|    22   (0)| 00:00:01 |
---------------------------------------------------------------------------------

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/30126024/viewspace-2143348/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/30126024/viewspace-2143348/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值