2009-01-20 24 views
5

Tony Andrews在另一個question給出的例子:這個實現符合SQL-92嗎?

IF p_c_courtesies_cd 
    || p_c_language_cd 
    || v_c_name 
    || v_c_firstname 
    || v_c_function 
    || p_c_phone 
    || p_c_mobile p_c_fax 
    || v_c_email is not null 
THEN 
    -- Do something 
END IF; 

作爲一個聰明的(如果不是一點點晦澀)替代到Oracle COALESCE函數。果然,它的工作原理,如果任何參數不爲null,IF測試是真實的。我的問題:Oracle的上述串聯操作SQL-92是否符合?不應該包含NULL的表達式計算爲NULL嗎?如果你不這麼認爲,那麼爲什麼表達式1 + NULL的計算結果爲NULL?

回答

3

不,甲骨文對空值的處理是特殊的,與其他人不同,並且與ANSI標準不一致。然而,在甲骨文的辯護中,它很可能已經解決並且早在ANSI標準與之一致之前就致力於這種治療!

這一切都從Oracle存儲帶有字符計數字符串和字符串數據的字符串開始。 NULL由字符計數0表示,沒有後續字符串數據 - 與空字符串('')完全相同。 Oracle根本無法區分它們。

這會導致一些古怪的行爲,例如這種串聯情況。 Oracle還有一個函數LENGTH來返回字符串的長度,但是這已經以相反的方式定義,所以LENGTH('')返回NULL而不是零。所以:

LENGTH('abc') + LENGTH('') IS NULL 

LENGTH('abc' || '') = 3 

這似乎違反了基本的數學原理。

當然,Oracle開發人員變得如此習慣於這樣,我們許多人甚至不能看到任何錯誤或奇怪的事情 - 有些人實際上會爭辯說世界其他地方是錯誤的,而且一個空的字符串和一個NULL 一樣的東西!

1

那麼COALESCE是由SQL-92標準顯式定義的,以返回列表中的第一個非NULL值;所以根據定義,Oracle的這個實現是正確的。

編輯:SQL-92 spec;搜索COALESCE以查看其定義。

這就是說,NULL沒有任何特定的說明,任何涉及NULL的操作都必須爲NULL。更確切的限制是NULL既不爲0也不等於另一個NULL(例如,NULL == NULL爲假,因爲一個NULL不等於另一個NULL)。但是,這並不意味着,不能始終返回NULL的邏輯上一致的NULL處理方式仍然存在。

編輯:所以NULL + 1是NULL,就像NaN + 1仍然是NaN一樣;它實際上是一個未定義的操作。

+0

+1爲鏈接。然而,正如我讀到的那樣,標準明確指出將空值連接到任何值的結果應該爲空。 – DCookie 2009-01-20 19:07:10

+0

@DCookie:啊,我以爲你在問COALESCE的執行是否符合標準。但是,我同意,如果它爲此返回非NULL,則它們的CONCATENATION實現不符合標準。 – nezroy 2009-01-20 19:26:34

+0

對不起,沒有拼寫出來,我想我從我給出的例子中,我所指的是串聯操作假設:-( – DCookie 2009-01-20 20:56:34

0
SQL> select 'something'||null from dual; 

'SOMETHIN 
--------- 
something 

帶空字符串連接不會導致null。我認爲這是正常的行爲,我習慣了。不知道還有什麼要說的。

2

@Nezroy:感謝您的鏈接。然而,在我閱讀標準時,我認爲它表明Oracle的實現實際上是錯誤的。第6.13節,一般規定,項目2A:

 2) If <concatenation> is specified, then let S1 and S2 be the re- 
     sult of the <character value expression> and <character factor>, 
     respectively. 

     Case: 

     a) If either S1 or S2 is the null value, then the result of the 
      <concatenation> is the null value. 
1

基礎上SQL-92規範由DCookie突出的部分和其他DB的行爲,我會說甲骨文是不是行爲標準與他們連接運算符。

甲骨文(從tuinstoel的答案):

SQL> select 'something'||null from dual; 

'SOMETHIN 
--------- 
something

MSSQL:

SELECT 'something'+NULL; 

NULL

的PostgreSQL:

postgres=# \pset null '(null)' 
Null display is "(null)". 
postgres=# select 'something'||null as output; 
output 
-------- 
(null) 
(1 row) 

的MySQL:

mysql> select concat('something',NULL) as output; 
+--------+ 
| output | 
+--------+ 
| NULL | 
+--------+ 
1 row in set (0.00 sec)