<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當前位置: 首頁 - 科技 - 知識百科 - 正文

        Oracle中關于null值的概念以及邏輯運算

        來源:懂視網 責編:小采 時間:2020-11-09 07:26:36
        文檔

        Oracle中關于null值的概念以及邏輯運算

        Oracle中關于null值的概念以及邏輯運算:null介紹 NULL是數據庫中特有的數據類型,當一條記錄的某個列為NULL,則表示這個列的是未知的、是不確定的。既然是未知的,就有無數種的可能性。因此,NULL并不是一個確定的。 這是NULL的由來、也是NULL的基礎,所有和NULL相關的操作的結果都可以從NULL的
        推薦度:
        導讀Oracle中關于null值的概念以及邏輯運算:null介紹 NULL是數據庫中特有的數據類型,當一條記錄的某個列為NULL,則表示這個列的是未知的、是不確定的。既然是未知的,就有無數種的可能性。因此,NULL并不是一個確定的。 這是NULL的由來、也是NULL的基礎,所有和NULL相關的操作的結果都可以從NULL的

        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。
        所以,很多時候會這樣總結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 < 0 THEN
        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 < 0結果都是未知,所以兩個判斷的結果都是NULL。最終,在屏幕上輸出的都是ELSE后面跟的輸出值。
        由于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。因此任何記錄都是可以插入的

        聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        Oracle中關于null值的概念以及邏輯運算

        Oracle中關于null值的概念以及邏輯運算:null介紹 NULL是數據庫中特有的數據類型,當一條記錄的某個列為NULL,則表示這個列的是未知的、是不確定的。既然是未知的,就有無數種的可能性。因此,NULL并不是一個確定的。 這是NULL的由來、也是NULL的基礎,所有和NULL相關的操作的結果都可以從NULL的
        推薦度:
        標簽: 數據 的含義 邏輯
        • 熱門焦點

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 亚洲免费视频在线观看| 中文字幕av免费专区| 五月亭亭免费高清在线| 国产亚洲精品观看91在线| 日本一区午夜艳熟免费| 亚洲AV无码不卡无码| 午夜免费啪视频在线观看 | 在线亚洲精品福利网址导航| 99在线在线视频免费视频观看| 久久精品国产亚洲AV不卡| 国产免费久久精品99久久| 久久久久亚洲AV无码专区网站| 美女网站在线观看视频免费的 | 亚洲人精品午夜射精日韩| 国产在线精品观看免费观看| 国产亚洲精品无码成人| 久久久99精品免费观看| 亚洲国产电影在线观看| 人妻视频一区二区三区免费| 亚洲国产AV无码一区二区三区| 又粗又大又长又爽免费视频| 中文字幕免费在线观看动作大片 | 午夜性色一区二区三区免费不卡视频| 亚洲人成影院在线高清| 国产一区二区三区免费看| 中文字幕不卡免费视频| 亚洲天天做日日做天天看| 国产v精品成人免费视频400条| 亚洲欧美黑人猛交群| 久久久久亚洲精品中文字幕| 国产免费无码一区二区| 亚洲AV无码国产精品色| 免费少妇a级毛片人成网| 成人片黄网站色大片免费观看APP| 亚洲精品自拍视频| 国产又大又长又粗又硬的免费视频| 中文字幕永久免费| 亚洲综合色7777情网站777| 亚洲国产精品成人一区| 永久在线免费观看| 免费无码婬片aaa直播表情|