查看: 42123|回復: 64

[精華] Oracle中的NULL

[復制鏈接]
論壇徽章:
226
BLOG每日發帖之星
日期:2010-02-11 01:01:06紫蛋頭
日期:2013-01-12 23:45:222013年新春福章
日期:2013-02-25 14:51:24問答徽章
日期:2013-10-17 18:06:40優秀寫手
日期:2013-12-18 09:29:10馬上有車
日期:2014-02-19 11:55:14馬上有房
日期:2014-02-19 11:55:14馬上有錢
日期:2014-02-19 11:55:14馬上有對象
日期:2014-02-19 11:55:14馬上加薪
日期:2014-02-19 11:55:14
跳轉到指定樓層
1#
發表于 2008-1-25 10:08 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
最近在論壇上經?吹,很多人提出和NULL有關的問題。
NULL其實是數據庫中特有的類型,它的特殊性使得在查詢、處理、比較NULL值時和其他數據與眾不同。
而且對NULL不理解的話,很容易出現錯誤,Oracle中很多容易出現的錯誤都是和NULL有關的。

以前寫過一個關于NULL的專題,在這里整理發表出來,希望對大家更好的理解NULL有所幫助。

[ 本帖最后由 yangtingkun 于 2008-1-25 10:25 編輯 ]
論壇徽章:
226
BLOG每日發帖之星
日期:2010-02-11 01:01:06紫蛋頭
日期:2013-01-12 23:45:222013年新春福章
日期:2013-02-25 14:51:24問答徽章
日期:2013-10-17 18:06:40優秀寫手
日期:2013-12-18 09:29:10馬上有車
日期:2014-02-19 11:55:14馬上有房
日期:2014-02-19 11:55:14馬上有錢
日期:2014-02-19 11:55:14馬上有對象
日期:2014-02-19 11:55:14馬上加薪
日期:2014-02-19 11:55:14
2#
 樓主| 發表于 2008-1-25 10:09 | 只看該作者
這一篇主要打算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的時候就不會出錯。
說了怎么多,來看一個經典的例子:
[PHP]

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  /
過程已創建。

.
[/PHP]

上面兩個過程是否是等價的?對于熟悉C或JAVA的開發人員來說,可能認為二者是等價的,但是在數據庫中,則還要考慮到NULL的情況。
當輸入為NULL時,可以看到上面兩個過程不同的輸出:

[PHP]

SQL> SET SERVEROUT ON
SQL> EXEC P1(NULL)
FALSE
PL/SQL 過程已成功完成。
SQL> EXEC P2(NULL)
TRUE
PL/SQL 過程已成功完成。

.
[/PHP]

輸入為NULL時,上面兩個過程中的判斷的結果都是一樣的,不管是NULL >= 0還是NULL < 0結果都是未知,所以兩個判斷的結果都是NULL。最終,在屏幕上輸出的都是ELSE后面跟的輸出值。
由于NULL所具有的特殊性,在處理數據庫相關問題時應該對NULL的情況額外考慮,否則很容易造成錯誤。

原文出自:http://space.itpub.net/4227/viewspace-69105

[ 本帖最后由 yangtingkun 于 2008-1-25 10:11 編輯 ]

使用道具 舉報

回復
論壇徽章:
226
BLOG每日發帖之星
日期:2010-02-11 01:01:06紫蛋頭
日期:2013-01-12 23:45:222013年新春福章
日期:2013-02-25 14:51:24問答徽章
日期:2013-10-17 18:06:40優秀寫手
日期:2013-12-18 09:29:10馬上有車
日期:2014-02-19 11:55:14馬上有房
日期:2014-02-19 11:55:14馬上有錢
日期:2014-02-19 11:55:14馬上有對象
日期:2014-02-19 11:55:14馬上加薪
日期:2014-02-19 11:55:14
3#
 樓主| 發表于 2008-1-25 10:09 | 只看該作者
這一篇介紹NULL布爾運算特點。


上一篇文章中介紹了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呢?下面通過一個過程來進行說明:
[PHP]
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
.
[/PHP]

由于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

最后,仍然來看一個例子:
[PHP]
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);
未選定行
.
[/PHP]

對于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 于 2008-1-25 10:27 編輯 ]

使用道具 舉報

回復
論壇徽章:
226
BLOG每日發帖之星
日期:2010-02-11 01:01:06紫蛋頭
日期:2013-01-12 23:45:222013年新春福章
日期:2013-02-25 14:51:24問答徽章
日期:2013-10-17 18:06:40優秀寫手
日期:2013-12-18 09:29:10馬上有車
日期:2014-02-19 11:55:14馬上有房
日期:2014-02-19 11:55:14馬上有錢
日期:2014-02-19 11:55:14馬上有對象
日期:2014-02-19 11:55:14馬上加薪
日期:2014-02-19 11:55:14
4#
 樓主| 發表于 2008-1-25 10:09 | 只看該作者
這一篇繼續介紹NULL布爾運算特點。


本來打算繼續寫NULL的其他特點的,結果從fuyuncat的回復中,我發現上一篇文章居然漏掉了一部分內容,只好在這里續一篇了。
上一篇討論AND和OR的情況,下面討論一下NULL的布爾值運算NOT。
對于TRUE和FALSE的NOT運算很簡單,NOT TRUE=FALSE,NOT FALSE=TRUE,那么如果包含NULL的情況呢,首先還是用事實來說話:
[php]
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 過程已成功完成。
.
[/php]

現在我們看到了一個很有趣的結果,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


原文出自:http://space.itpub.net/4227/viewspace-69109


[ 本帖最后由 yangtingkun 于 2008-1-25 10:26 編輯 ]

使用道具 舉報

回復
論壇徽章:
226
BLOG每日發帖之星
日期:2010-02-11 01:01:06紫蛋頭
日期:2013-01-12 23:45:222013年新春福章
日期:2013-02-25 14:51:24問答徽章
日期:2013-10-17 18:06:40優秀寫手
日期:2013-12-18 09:29:10馬上有車
日期:2014-02-19 11:55:14馬上有房
日期:2014-02-19 11:55:14馬上有錢
日期:2014-02-19 11:55:14馬上有對象
日期:2014-02-19 11:55:14馬上加薪
日期:2014-02-19 11:55:14
5#
 樓主| 發表于 2008-1-25 10:09 | 只看該作者
這一篇描述NULL的字符串表示格式’’。

發現很多人對空字符串’’不是很清楚,這里簡單總結一下。
以前我總說空字符串’’等價于NULL,但是有些人喜歡鉆牛角尖,所以我改一下說法,空字符串’’是NULL的字符類型的表現格式。
也許有人會認為,NULL就是NULL,本身沒有類型的一說,但是我認為,NULL還是有類型的,只不過不同類型的NULL都用相同的關鍵字NULL來表示。而且,NULL本身也可以轉化為任意類型的數據,因此給人的感覺是NULL沒有數據類型。
其實NULL不但有數據類型,還有默認的數據類型,那就是字符類型。至于這個答案是如何推斷出來的,請看:http://yangtingkun.itpub.net/post/468/50132
不過上面說的這個默認的數據類型是在極限的情況下測試出來的,如果只是給出一個NULL,那么它是可以代表任意的類型的。
證明空字符串就是NULL是很容易的:

[php]

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

.
[/php]

上面三個SQL語句,任意一個都足以證明空字符串’’就是NULL。
有些人可能會說,既然’’就是NULL,為什么不能進行IS ’’的判斷呢?

[php]

SQL> SELECT 1 FROM DUAL WHERE '' IS '';
SELECT 1 FROM DUAL WHERE '' IS ''
                               *
第 1 行出現錯誤:
ORA-00908: 缺失 NULL 關鍵字

.
[/php]

其實從上面的錯誤信息就可以看到答案。原因就是IS NULL是Oracle的語法,在Oracle運行的時刻’’是NULL,但是現在Oracle還沒有運行這句SQL,就由于語法不正確被SQL分析器擋住了。Oracle的語法并不包含IS ’’的寫法,所以,這一點并不能稱為’’不是NULL的理由。
那么我為什么還要說’’是NULL的字符表示形式呢?因為’’和NULL還確實不完全一樣,對于NULL來說,它表示了各種數據類型的NULL值。而對于空字符串’’來說,雖然它也具有NULL的可以任意轉化為其他任何數據類型的特點,但是無論是從形式上還是從本質上它都表現出了字符類型的特點。
下面通過一個例子來證明’’本質是字符類型的NULL。

[php]

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' 聲明與此次調用相匹配
.
[/php]

從這一點上可以看出’’實際上已經具備了數據類型。所以我將’’表述為空字符串是NULL的字符類型表現形式。

原文出自:http://space.itpub.net/4227/viewspace-69110

[ 本帖最后由 yangtingkun 于 2008-1-25 10:30 編輯 ]

使用道具 舉報

回復
論壇徽章:
226
BLOG每日發帖之星
日期:2010-02-11 01:01:06紫蛋頭
日期:2013-01-12 23:45:222013年新春福章
日期:2013-02-25 14:51:24問答徽章
日期:2013-10-17 18:06:40優秀寫手
日期:2013-12-18 09:29:10馬上有車
日期:2014-02-19 11:55:14馬上有房
日期:2014-02-19 11:55:14馬上有錢
日期:2014-02-19 11:55:14馬上有對象
日期:2014-02-19 11:55:14馬上加薪
日期:2014-02-19 11:55:14
6#
 樓主| 發表于 2008-1-25 10:09 | 只看該作者
這一篇繼續討論空字符’’和NULL的關系。


上面一篇文章中,已經從事實的角度證明了空字符就是NULL的字符表現形式。這一篇將試圖解釋為什么空字符就是NULL。而且準備簡單描述一下字符串合并操作||的特殊性。
根據NULL的定義,NULL是不確定、未知的含義,那么為什么字符類型的NULL是一個空字符呢?而且,對于NULL的加、減、乘、除等操作的結果都是NULL,而為什么字符串合并操作||,當輸入字符串有一個為空時,不會得到結果NULL。

[PHP]

SQL> SELECT NULL || 'A', 'B' || NULL, NULL || NULL FROM DUAL;
NU ' N
-- - -
A  B

.
[/PHP]

上面兩個問題需要從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。

[PHP]

SQL> SELECT * FROM DUAL WHERE CHR(0) = CHR(0);
D
-
X
SQL> SELECT * FROM DUAL WHERE CHR(0) IS NULL;
未選定行

.
[/PHP]

原文出自:http://space.itpub.net/4227/viewspace-69124

[ 本帖最后由 yangtingkun 于 2008-1-25 10:33 編輯 ]

使用道具 舉報

回復
論壇徽章:
226
BLOG每日發帖之星
日期:2010-02-11 01:01:06紫蛋頭
日期:2013-01-12 23:45:222013年新春福章
日期:2013-02-25 14:51:24問答徽章
日期:2013-10-17 18:06:40優秀寫手
日期:2013-12-18 09:29:10馬上有車
日期:2014-02-19 11:55:14馬上有房
日期:2014-02-19 11:55:14馬上有錢
日期:2014-02-19 11:55:14馬上有對象
日期:2014-02-19 11:55:14馬上加薪
日期:2014-02-19 11:55:14
7#
 樓主| 發表于 2008-1-25 10:09 | 只看該作者
這篇文檔討論一下NULL和索引的關系。

前面幾篇文章討論了NULL的數據類型和NULL的運算特點。這里打算簡單描述NULL和索引的關系。
如果說NULL類型已經比較容易出錯了,那么索引問題就讓NULL又一次成為問題的焦點。
大多數人都聽說過這樣一句話,索引不存儲NULL值。這句話其實比不嚴謹。如果采用比較嚴謹的方式來說:B樹索引不存儲索引列全為空的記錄。如果把這句話用在單列索引上,就是前面提到的B樹索引不存儲NULL。
首先索引分為BTREE和BITMAP兩種,對于BTREE索引,是不存儲NULL值的,而對于BITMAP索引,是存儲NULL值的。
而從索引列的個數來劃分,索引非為單列索引和復合索引,對于單列索引來說很簡單,如果一條記錄中這個索引字段為空,那么索引不會保存這條記錄的信息。但是對于復合索引,由于存在著多個列,如果某一個索引列不為空,那么索引就會包括這條記錄,即使其他所有的所有列都是NULL值。

[PHP]

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 |
-------------------------------------------------------------------

.
[/PHP]

Oracle的優化器在確定是否使用索引的時候,第一標準是能否得到一個正確的結果。由于OBJECT_ID是可以為空的,而索引列不包含為空的記錄。因此通過索引掃描無法得到一個正確的結果,這就是SELECT COUNT(*) FROM T不會使用OBJECT_ID上的索引的原因。
而對于BITMAP索引,則是另外的情況:

[PHP]

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)

.
[/PHP]

從上面的結果不難看出BITMAP索引中是包含NULL的。
下面看看復合索引的情況:

[PHP]

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)

.
[/PHP]

雖然結果中包含了NULL值,但是Oracle并沒有讀取表,而僅僅通過索引掃描就返回了結果,這說明復合索引中是可能包含NULL值的。
本文簡單說明了索引和NULL值的關系。這里并沒有對反鍵索引(reverse)、逆序索引(desc)、函數索引(FBI)和CLUSTER索引進行說明。
原因是這些索引其實都屬于離不開BTREE索引和BITMAP索引的范疇。不必關心索引是否倒序或反鍵,只要是BTREE索引,就不會存儲全NULL記錄,反之,只要是BITMAP索引就會存儲NULL值。
唯一需要注意的是函數索引,函數索引的真正索引列是函數的計算結果而不是行記錄中的數據,清楚了這一點函數索引其實和普通索引就沒有什么區別了。
最后說明一下域索引。由于域索引的實現本身可能會很復雜,Oracle可能在內部是用一套表和過程來實現的,因此對于域索引是否存儲NULL,要根據域索引的實現去進行具體的分析了。

原文出自:http://space.itpub.net/4227/viewspace-69139

[ 本帖最后由 yangtingkun 于 2008-1-25 10:35 編輯 ]

使用道具 舉報

回復
論壇徽章:
226
BLOG每日發帖之星
日期:2010-02-11 01:01:06紫蛋頭
日期:2013-01-12 23:45:222013年新春福章
日期:2013-02-25 14:51:24問答徽章
日期:2013-10-17 18:06:40優秀寫手
日期:2013-12-18 09:29:10馬上有車
日期:2014-02-19 11:55:14馬上有房
日期:2014-02-19 11:55:14馬上有錢
日期:2014-02-19 11:55:14馬上有對象
日期:2014-02-19 11:55:14馬上加薪
日期:2014-02-19 11:55:14
8#
 樓主| 發表于 2008-1-25 10:09 | 只看該作者
這一篇討論NULL對SQL使用索引的影響。

最近在論壇上看到很多人從網上找到一些SQL優化的注意點,其中有不少就和NULL的處理有關。遺憾的是,找到的優化文章都是比較老的,一般只適用于RBO下,而對CBO以及不再適用了。
本文關注點仍然是在NULL上面,這篇文章打算通過實例來說明這些文章中和NULL相關的一些觀點已經不適用于CBO優化器了。
觀點一:判斷一個列IS NOT NULL不會使用索引。
其實這個觀點從一般意義上也解釋不同,因為B樹索引本身不存儲鍵值全為NULL的記錄,所以通過索引掃描得到的結果一定滿足IS NOT NULL的要求。

[PHP]

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

.
[/PHP]

由于索引的存儲特性和IS NOT NULL訪問本身沒有沖突,因此,這種情況下很容易通過索引來得到相應的結果。
觀點二:判斷一個列IS NULL不會使用索引。
這里不討論BITMAP索引。由于BITMAP索引保存NULL值,所以討論BITMAP索引沒有意義。這里僅討論B樹索引。
在上一篇文章里面其實已經討論過了,B樹索引不存儲鍵值全為空的記錄。所以對于單列索引而言,確實IS NULL操作是無法使用索引的。但是復合索引是可能存儲一部分NULL值的,所以,IS NULL操作也并非不可能使用索引。

[PHP]
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

.
[/PHP]

從上面的兩個例子可以看到,Oracle的CBO并不會因為SQL語句中指定了IS NOT NULL或IS NULL操作就不再使用索引。CBO選擇索引的條件只有正確性和代價,只要滿足這兩個條件就可以了。


原文出自:http://space.itpub.net/4227/viewspace-69153

[ 本帖最后由 yangtingkun 于 2008-1-25 10:37 編輯 ]

使用道具 舉報

回復
論壇徽章:
226
BLOG每日發帖之星
日期:2010-02-11 01:01:06紫蛋頭
日期:2013-01-12 23:45:222013年新春福章
日期:2013-02-25 14:51:24問答徽章
日期:2013-10-17 18:06:40優秀寫手
日期:2013-12-18 09:29:10馬上有車
日期:2014-02-19 11:55:14馬上有房
日期:2014-02-19 11:55:14馬上有錢
日期:2014-02-19 11:55:14馬上有對象
日期:2014-02-19 11:55:14馬上加薪
日期:2014-02-19 11:55:14
9#
 樓主| 發表于 2008-1-25 10:09 | 只看該作者
這一篇描述一下在SQL和PLSQL中一些處理NULL的一些問題。

NULL的最大的特點就是兩個NULL是不相等的。如果用等號來判斷兩個NULL是否相等得到的結果一定是NULL。從唯一約束的特點也可以看到,對于建立了唯一約束的列,Oracle允許插入多個NULL值,這時因為Oracle不認為這些NULL是相等的。

[PHP]

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 行。

.
[/PHP]

但是有的時候,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值。

[PHP]

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 行。

.
[/PHP]

聚集函數中比較特殊的是COUNT,第一個特殊點是COUNT不會返回NULL值,即使表中沒有記錄,或者COUNT(COL)中,COL列的記錄全為NULL,COUNT也會返回0值而不是NULL。第二個特殊點就是COUNT(*)或COUNT(常量)的形式。這種形式使得COUNT可以計算包含NULL記錄在內的記錄總數。

[PHP]

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

.
[/PHP]

最后簡單說一下AVG,AVG(COL)等價于SUM(COL)/COUNT(COL),不等價于SUM(COL)/COUNT(*):

[PHP]

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

.
[/PHP]

原文出自:http://space.itpub.net/4227/viewspace-69167

[ 本帖最后由 yangtingkun 于 2008-1-25 10:39 編輯 ]

使用道具 舉報

回復
論壇徽章:
226
BLOG每日發帖之星
日期:2010-02-11 01:01:06紫蛋頭
日期:2013-01-12 23:45:222013年新春福章
日期:2013-02-25 14:51:24問答徽章
日期:2013-10-17 18:06:40優秀寫手
日期:2013-12-18 09:29:10馬上有車
日期:2014-02-19 11:55:14馬上有房
日期:2014-02-19 11:55:14馬上有錢
日期:2014-02-19 11:55:14馬上有對象
日期:2014-02-19 11:55:14馬上加薪
日期:2014-02-19 11:55:14
10#
 樓主| 發表于 2008-1-25 10:09 | 只看該作者
NULL的數據類型

Oracle的NULL代表的含義是不確定,那么不確定的東西也會有確定的數據類型嗎?或者換個說法,NULL在Oracle中的默認數據類型是什么,下面就來探討這個問題。

首先公布答案,NULL的默認類型是字符類型,具體是VARCHAR2還是CHAR,這個并不清楚,不過我個人懷疑是VARCHAR2的可能性更大一些。
我們知道一個字段不管是何種類型的,都可以插入NULL值,也就是說,NULL可以隨意的轉換為任意的類型。
而且,絕大部分的函數輸入值為NULL,返回的結果也為NULL,這就阻止了我們通過函數的返回結果判斷NULL的類型的企圖。我們最常用來分析數據的DUMP函數,這回也實效了:

[PHP]

SQL> SELECT DUMP(NULL) FROM DUAL;
DUMP
----
NULL
而且試圖通過CREATE TABLE AS來判定NULL的類型也是不可能的:
SQL> CREATE TABLE T AS SELECT TNAME, NULL COL1 FROM TAB;
CREATE TABLE T AS SELECT TNAME, NULL COL1 FROM TAB
                                *
ERROR 位于第 1 行:
ORA-01723: 不允許長度為 0 的列

.
[/PHP]

可能有人會產生疑問,既然各種方法的行不通,你是怎么得到NULL的默認類型的?也許還有人會想,既然NULL可以隱式的轉化為任意的類型,討論NULL的默認類型是否有意義呢?
下面就是我發現NULL的數據類型的例子,同時說明了如果不注意NULL的數據類型可能會出現的問題。
由于原始的SQL過于復雜,我這里給出一個簡化的例子。

[PHP]

SQL> create table t (id number);
表已創建。
SQL> insert into t values (1);
已創建 1 行。
SQL> insert into t values (8);
已創建 1 行。
SQL> insert into t values (0);
已創建 1 行。
SQL> insert into t values (15);
已創建 1 行。
SQL> commit;
提交完成。

.
[/PHP]

需要按照T中的ID的升序顯示數據,SQL如下:

[PHP]

SQL> select * from t order by id;
        ID
----------
         0
         1
         8
        15

.
[/PHP]

需求還有一點點小的要求,對于0值這個比較特殊的值,在所有非0值的后面顯示。當然實現的方法比較多,比如使用UNION ALL將非0值和0值分開,或者將0值轉換為一個很大的數值。
由于ID的最大值不確定,且考慮使用一個簡單的SQL完成,我選擇了在排序的時候將0值轉化為NULL的方法,這樣利用排序時NULL最大的原理,得到我希望的結果。
SQL如下:

[PHP]

SQL> select * from t order by decode(id, 0, null, id);
        ID
----------
         1
        15
         8
         0

.
[/PHP]

0值確實如我所愿排在了最后,但是結果怎么“不對”了!

[PHP]

SQL> select decode(id, 0, null, id) from t;
DECODE(ID,0,NULL,ID)
----------------------------------------
1
8
15


.
[/PHP]

看看DECODE函數的結果,這回明白了,原來DECODE的結果變為了字符類型。字符類型結果在SQLPLUS顯示左對齊,而數值類型是右對齊。
在DECODE函數中,輸入的4個參數中兩個ID和0都是NUMBER類型,只有NULL這一個輸入值類型不確定,莫非是由于NULL的類型是字符類型?
猜測只是猜測,還需要確切的證據證明這一點,下面看看標準包中DECODE函數的定義。
下面的DECODE函數定義是從STANDARD中摘取出來的部分內容:

[PHP]

function DECODE (expr NUMBER, pat NUMBER, res NUMBER) return NUMBER;
function DECODE (expr NUMBER,
                 pat NUMBER,
                 res VARCHAR2 CHARACTER SET ANY_CS)
      return VARCHAR2 CHARACTER SET res%CHARSET;
function DECODE (expr NUMBER, pat NUMBER, res DATE) return DATE;
function DECODE (expr VARCHAR2 CHARACTER SET ANY_CS,
                 pat VARCHAR2 CHARACTER SET expr%CHARSET,
                 res NUMBER) return NUMBER;
function DECODE (expr VARCHAR2 CHARACTER SET ANY_CS,
                 pat VARCHAR2 CHARACTER SET expr%CHARSET,
                 res VARCHAR2 CHARACTER SET ANY_CS)
      return VARCHAR2 CHARACTER SET res%CHARSET;
function DECODE (expr VARCHAR2 CHARACTER SET ANY_CS,
                 pat VARCHAR2 CHARACTER SET expr%CHARSET,
                 res DATE) return DATE;
function DECODE (expr DATE, pat DATE, res NUMBER) return NUMBER;
function DECODE (expr DATE,
                 pat DATE,
                 res VARCHAR2 CHARACTER SET ANY_CS)
      return VARCHAR2 CHARACTER SET res%CHARSET;
function DECODE (expr DATE, pat DATE, res DATE) return DATE;

.
[/PHP]

通過觀察上面的定義,我們不難發現,雖然Oracle對DECODE函數進行了大量的重載,且DECODE函數支持各種的數據類型,但是DECODE函數具有一個規律,就是DECODE函數的返回值的類型和DECODE函數的輸入參數中第一個用來返回的參數的數據類型一致?赡懿惶美斫,舉個簡單的例子:

[PHP]

SQL> select decode(id, 1, '1', 2) from t;
D
-
1
2
2
2
SQL> select decode(id, '1', 1, '2') from t;
DECODE(ID,'1',1,'2')
--------------------
                   1
                   2
                   2
                   2

.
[/PHP]

從這兩個簡單的例子就可以看出,DECODE的返回值的數據類型和DECODE函數中第一個表示返回的參數的數據類型一致。
從這點就可以看出,NULL的默認數量類型是字符類型,這才導致DECODE的結果變成了字符串,而查詢根據字符串的排序比較,因此’15’小于’8’。
知道了問題的原因,解決的方法就很多了,比如:

[PHP]

SQL> select * from t order by decode(id, 1, 1, 0, null, id);
        ID
----------
         1
         8
        15
         0
SQL> select * from t order by to_number(decode(id, 0, null, id));
        ID
----------
         1
         8
        15
         0
SQL> select * from t order by decode(id, 0, cast(null as number), id);
        ID
----------
         1
         8
        15
         0
SQL> select * from t order by decode(id, 0, to_number(null), id);
        ID
----------
         1
         8
        15
         0

.
[/PHP]

原文出自:http://space.itpub.net/4227/viewspace-68731

[ 本帖最后由 yangtingkun 于 2008-1-25 10:43 編輯 ]

使用道具 舉報

回復

您需要登錄后才可以回帖 登錄 | 注冊

本版積分規則 發表回復

TOP技術積分榜 社區積分榜 徽章 團隊 統計 知識索引樹 積分競拍 文本模式 幫助
  ITPUB首頁 | ITPUB論壇 | 數據庫技術 | 企業信息化 | 開發技術 | 微軟技術 | 軟件工程與項目管理 | IBM技術園地 | 行業縱向討論 | IT招聘 | IT文檔
  ChinaUnix | ChinaUnix博客 | ChinaUnix論壇
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓優訊信息技術有限公司版權所有 聯系我們 
京ICP備09055130號-4  北京市公安局海淀分局網監中心備案編號:11010802021510 廣播電視節目制作經營許可證:編號(京)字第1149號
  
快速回復 返回頂部 返回列表
30岁的男人干啥赚钱快赚钱多 一二分钟开奖的是什么彩 宁夏十一选五走势今天 贵州11选5任五遗漏一定牛 模拟炒股 胆码4个 拖码4 股票推荐王新专业 吉林十一选五前三走 安徽快3开奖号码今天 看三来四必中特打一肖 苏快三走势图今天