首页 > 代码库 > NULL 的问题

NULL 的问题

/*************************************************
二、主题:NULL 的问题

*************************************************/
    CREATE TABLE TEST1
    (  
        ID   NUMBER(1) NOT NULL PRIMARY KEY,
        NAME VARCHAR(10)
    );
    CREATE unique INDEX IDX_TEST1 ON TEST1(NAME);
   
    INSERT INTO TEST1 VALUES(0,‘0‘);
    INSERT INTO TEST1 VALUES(1,‘1‘);
    INSERT INTO TEST1 VALUES(2,‘2‘);
    INSERT INTO TEST1 VALUES(3,‘A‘);
    INSERT INTO TEST1 VALUES(4,‘B‘);
    INSERT INTO TEST1 VALUES(5,‘a‘);  
    INSERT INTO TEST1(ID)  VALUES(7);  
   
=============================================================================
2.1.UNKNOWN 与 空格、0、其他不可见字符等不同

    SELECT DUMP(NULL) AS A,
           DUMP(‘‘) AS B,
           0 AS C
      FROM DUAL;--结果:NULL NULL 0
    --NULL 与空字符串,NULL是无类型或者说是任何类型的。
    SELECT CASE WHEN ‘‘  = NULL THEN 1 ELSE 0 END AS COP1,  --引号内无内容,结果为:0
           CASE WHEN ‘ ‘ = NULL THEN 1 ELSE 0 END AS COP2  --引号内是空格,结果为:0
      FROM DUAL; 
     
2.2.任何与NULL的比较操作,都返回NULL。

    --返回nuknown 值  
    SELECT * FROM TEST1 WHERE ID=NULL;
    SELECT * FROM TEST1 WHERE ID<>NULL;
   
    --返回正确查询
    SELECT * FROM TEST1 WHERE ID IS NOT NULL;
    SELECT * FROM TEST1 WHERE NAME IS NULL;
   
2.3.算术运算
   
    SELECT 10+NULL,10*NULL,10/NULL FROM DUAL;    --结果:nuknown nuknown nuknown
    SELECT ‘A‘|| NULL,CONCAT(NULL,‘a‘) FROM DUAL;--结果忽略NULL:A       a
   
2.4.NULL相关函数
 2.4.1. NVL(VALUE,REPLACEVALUE),替换NULL为某值,但是需要替换的值类型必须与之一致。
 
    SELECT ID , NVL(NAME,‘NoValue‘) FROM TEST1;
   
 2.4.2. NVL2(VALUE,EXPR2,EXPR3),若VALUE为空,则返回EXPR3,否则返回EXPR2 ,EXPR2 与EXPR3的类型保持一致。
 
    SELECT ID,NVL2(NAME,1,‘2‘) FROM TEST1; --若NAME字段不为空,则返回1,若为空则返回2,,此处返回的是NUMBER类型,因为1比‘2‘ 的优先级高。
    SELECT ID,NVL2(NAME,1,NULL) FROM TEST1;--若NAME字段不为空,则返回1,否则返回null。
    SELECT ID,NVL2(NAME,NULL,‘Y‘) FROM TEST1;--若NAME字段不为空,则返回NULL,否则返回‘Y‘.
   
 2.4.3. NULLIF(VALUE1,VALUE2),若VALUE1=VALUE2,则返回NULL,否则返回VALUE1,即继续用VALUE1;强制要求:两个值类型必须一致。
 
    SELECT NULLIF(1,2) FROM DUAL; --结果:1
    SELECT NULLIF(1,1) FROM DUAL;
          
    --在除法中的作用 ,比较有用,此处用法可以替代DECODE,CASE 。
    SELECT  10/0  FROM DUAL;  --错误,因为除数不能为0,小学生常识。
    SELECT 10/NULLIF(0,0) FROM DUAL;--结果为空值   
   
    SELECT ID,10/CASE WHEN ID = 0 THEN NULL ELSE ID END ID2 FROM TEST1;--正确,ID=0 的得到空值
    SELECT ID,10/DECODE(ID,0,NULL,ID) ID2 FROM TEST1; --正确,ID=0 的得到空值
    SELECT ID,10/NULLIF(ID,0) FROM TEST1;--正确,ID=0 的得到空值
 
    --实际用得不多
    SELECT NULLIF(NULL,1) FROM DUAL;--报错
    SELECT NULLIF(TO_CHAR(NULL),‘1‘) FROM DUAL;--返回空值

    SELECT NULLIF(TO_NUMBER(NULL),1) FROM DUAL;   --报错
    SELECT NULLIF(TO_NUMBER(NULL,‘9‘),1) FROM DUAL;--返回空值  
 
 2.4.4. COALESCE (X1,X3,Xn...),从左往右取,得到第一个非空值,其中值类型必须一致,否则会报错。
   
    SELECT COALESCE(1,‘1‘) FROM DUAL;--报错,类型不一致
   
    SELECT COALESCE(NULL,1,2) FROM DUAL;--结果:1
   
 2.4.5. CASE WHEN ,DECODE 处理 NULL值 。CASE 表达式得到数值型的,DECODE得到字符型的。
    --建立测试表TEST3
    =====================================================
    create table TEST3
    (
      sgnyear       NVARCHAR2(4) not null,
      areacode      NVARCHAR2(10) not null,
      transtypecode NVARCHAR2(10) not null,
      cargovolume   NUMBER(18,4),
      updateid      NUMBER(18),
      updatestate   NUMBER(1) default 0,
      updatetime    DATE default SYSDATE,
      businesstime  DATE default SYSDATE
    )  ;
    --插入数据,类似数据
    insert into TEST3 (SGNYEAR, AREACODE, TRANSTYPECODE, CARGOVOLUME, UPDATEID, UPDATESTATE, UPDATETIME, BUSINESSTIME)
    values (‘2002‘, ‘360000‘, ‘5‘, 0.0000, 131715, 0, to_date(‘11-03-2014 10:54:08‘, ‘dd-mm-yyyy hh24:mi:ss‘), to_date(‘01-01-2002‘, ‘dd-mm-yyyy‘));

    insert into TEST3 (SGNYEAR, AREACODE, TRANSTYPECODE, CARGOVOLUME, UPDATEID, UPDATESTATE, UPDATETIME, BUSINESSTIME)
    values (‘2002‘, ‘150000‘, ‘6‘, 37239.0000, 112343, 0, to_date(‘11-03-2014 10:54:08‘, ‘dd-mm-yyyy hh24:mi:ss‘), to_date(‘01-01-2002‘, ‘dd-mm-yyyy‘));

    insert into TEST3 (SGNYEAR, AREACODE, TRANSTYPECODE, CARGOVOLUME, UPDATEID, UPDATESTATE, UPDATETIME, BUSINESSTIME)
    values (‘2011‘, ‘530000‘, ‘1‘, 60169.8590, 107295, 0, to_date(‘11-03-2014 10:54:08‘, ‘dd-mm-yyyy hh24:mi:ss‘), to_date(‘01-01-2011‘, ‘dd-mm-yyyy‘));

    insert into TEST3 (SGNYEAR, AREACODE, TRANSTYPECODE, CARGOVOLUME, UPDATEID, UPDATESTATE, UPDATETIME, BUSINESSTIME)
    values (‘1995‘, ‘330000‘, ‘3‘, 1914.0000, 111791, 0, to_date(‘11-03-2014 10:54:08‘, ‘dd-mm-yyyy hh24:mi:ss‘), to_date(‘01-01-1995‘, ‘dd-mm-yyyy‘));

    insert into TEST3 (SGNYEAR, AREACODE, TRANSTYPECODE, CARGOVOLUME, UPDATEID, UPDATESTATE, UPDATETIME, BUSINESSTIME)
    values (‘2008‘, ‘150000‘, ‘2‘, 38357.3386, 120727, 0, to_date(‘11-03-2014 10:54:08‘, ‘dd-mm-yyyy hh24:mi:ss‘), to_date(‘01-01-2008‘, ‘dd-mm-yyyy‘));
    .....
    =====================================================   
    --查询1.,根据年份分组,根据 TransTypeCode 为行列转换 标准,updatestate=2的数据视为无效数据
      select
             SgnYear,         
             MAX(case when updatestate=2 then null else decode(TransTypeCode , ‘1‘,ROUND(CargoVolume,2),null) end) as Et01,
             MAX(case when updatestate=2 then null else decode(TransTypeCode , ‘2‘,ROUND(CargoVolume,2),null) end) as Et02,
             MAX(case when updatestate=2 then null else decode(TransTypeCode , ‘3‘,ROUND(CargoVolume,2),null) end) as Et03,
             MAX(case when updatestate=2 then null else decode(TransTypeCode , ‘4‘,ROUND(CargoVolume,2),null) end) as Et04,
             MAX(case when updatestate=2 then null else decode(TransTypeCode , ‘5‘,ROUND(CargoVolume,2),null) end) as Et05,
             MAX(case when updatestate=2 then null else decode(TransTypeCode , ‘6‘,ROUND(CargoVolume,2),null) end) as Et06,
             MAX(case when updatestate=2 then null else decode(TransTypeCode , ‘7‘,ROUND(CargoVolume,2),null) end) as Et07,
             MAX(case when updatestate=2 then null else decode(TransTypeCode , ‘8‘,ROUND(CargoVolume,2),null) end) as Et08,
             MAX(case when updatestate=2 then null else decode(TransTypeCode , ‘9‘,ROUND(CargoVolume,2),null) end) as Et09,
             case when min(updatestate)=2 then 2 else 1 end as updatestate         
         from TEST3
        group by SgnYear ;  
    --得到的结果,值全为数值型。

    --查询2.与查询1一样的思想,换一种方法
      select
             SgnYear,         
             MAX(decode(TransTypeCode , ‘1‘,decode(UPDATESTATE,2,NULL,ROUND(CargoVolume,2)))) as Et01,
             MAX(decode(TransTypeCode , ‘2‘,decode(UPDATESTATE,2,NULL,ROUND(CargoVolume,2)))) as Et02,
             MAX(decode(TransTypeCode , ‘3‘,decode(UPDATESTATE,2,NULL,ROUND(CargoVolume,2)))) as Et03,
             MAX(decode(TransTypeCode , ‘4‘,decode(UPDATESTATE,2,NULL,ROUND(CargoVolume,2)))) as Et04,                          
             case when min(updatestate)=2 then 2 else 1 end as updatestate         
         from TEST3
        group by SgnYear ;
     --查询2,得到的结果,值全部为 字符型

     --比较
     SELECT  decode(UPDATESTATE,2,NULL,ROUND(CargoVolume,2)),--得到的值为字符型
             decode(TransTypeCode , ‘1‘,decode(UPDATESTATE,2,NULL,ROUND(CargoVolume,2))),--得到的值为字符型
             case when updatestate=2 then null else CargoVolume end,--得到的值为数值型
             case when updatestate=2 then null else decode(TransTypeCode , ‘1‘,ROUND(CargoVolume,2),null) end--得到的值为数值s型
        FROM TEST3;

2.5.NULL与索引
    基本索引(B-TREE),是不存储全为NULL的列的。复合索引若有非NULL的列可以存储,但是全为空不存储。
    =================================================      
    CREATE TABLE TEST2
    (
     ID    NUMBER,
     VALUE NUMBER
    );
    CREATE INDEX IDX1_TEST2 ON TEST2(ID);--索引不会存储NULL值

    INSERT INTO TEST2 VALUES(6,1);
    INSERT INTO TEST2 VALUES(2,2);
    INSERT INTO TEST2 VALUES(3,1);
    INSERT INTO TEST2 VALUES(4,NULL);
    INSERT INTO TEST2 VALUES(1,NULL);
    =================================================
       
    SELECT * FROM TEST2 WHERE ID IS NOT NULL;--走索引
    SELECT * FROM TEST2 WHERE ID IS NULL;--不走索引。
   
    -- IS NULL 的查询若要走索引,可以建立一个有伪列的索引
   
    --建立有伪列的索引
    CREATE INDEX IDX2_TEST2  ON TEST2(ID,2);
   
    -------------更新表的统计信息,若不更新,还是与之前一样
    SQL>  EXEC   DBMS_STATS.GATHER_TABLE_STATS(USER,‘TEST2‘,CASCADE=>TRUE) ;
 
    PL/SQL procedure successfully completed
    ---------------
   
    --再次查看执行计划
    SELECT * FROM TEST2 WHERE ID IS NOT NULL;--不走索引
    SELECT * FROM TEST2 WHERE ID IS NULL;    --走索引
   
    --此时,IS NOT NULL 的查询不走索引, IS NULL的查询走索引。 ^_^

2.6.建表时,NOT NULL 一定在DEFAULT 之后,不然会报错

2.7.NULL 与 排序
    --在ORDER BY 中 ,NULL 默认为最大值,所以ASC 时排在最后,DESC时排在最前面
    SELECT * FROM TEST1 ORDER BY NAME ASC;  --为NULL的排在后面
   
    SELECT * FROM TEST1 ORDER BY NAME DESC; --为NULL的排在前面
   
    --当然,可以显示制定NULL的排序规则
    SELECT * FROM TEST1 ORDER BY NAME ASC NULLS FIRST;--为NULL的排在前面
    SELECT * FROM TEST1 ORDER BY NAME DESC NULLS LAST;--为NULL的排在后面    
   
=============================================================================