2017-08-16 148 views
1

據Oracle的文檔,一個空不能相等或不相等的任何值或另一個空唯一性約束導致與空值

這在任何列中的唯一性約束的情況下是明顯的問題。但是,如果多列上的唯一性約束,行爲是不同的。例如:

CREATE TABLE table1 (
    col1 NUMBER(2), 
    col2 NUMBER(2), 
    CONSTRAINT uniq_col1_col2 UNIQUE (col1, col2) 
); 
INSERT INTO table1 VALUES (1, NULL); 
INSERT INTO table1 VALUES (1, NULL); 
# ORA-00001: unique constraint (XYZ.UNIQ_COL1_COL2) violated 

這是爲什麼?我怎樣才能指定約束忽略空值?

編輯

更具體地說,如果行(null), (null)是獨特的,爲什麼(1,null), (1,null)不是唯一的?這背後的理由是什麼?

+1

所以,如果它忽略了空值1 = 1,它會違反約束嗎? (它已經忽略了空值)等於和不等於引用'='和'<>。「不爲空」或「爲空」是空安全操作並且可以與空值進行比較。所以是空比較爲col2爲null,因此它們是相等的,所以是1 = 1,因爲所有數據匹配(重複) – xQbert

+0

你真的想發生什麼?這兩個插入將被允許,但兩個具有相同'col1'和相同非空'col2'的插入不會?你如何區分'col1 = 1'這兩行? –

回答

3

這是(強調)什麼the documentation says

爲了滿足唯一約束,表中沒有兩行可以有唯一鍵的值相同。但是,由單個列組成的唯一鍵可能包含空值。要滿足複合唯一鍵,表或視圖中的兩行不能在鍵列中具有相同的值組合。任何包含所有關鍵列中的空值的行都會自動滿足約束條件。 但是,包含一個或多個鍵列的空值的兩行以及其他鍵列的值的相同組合違反了約束。

它正在做它應該做的事情。使用兩個示例插入,兩個(潛在)行在一個鍵列中包含空值,在另一個鍵列中包含相同的值(1),因此違反了約束。

沒有別的東西真的有意義;無論如何,允許這兩個插入行爲都會給你帶來兩個不可區分的行。


你問:

更具體地說,如果行(null), (null)是獨特的,爲什麼(1,null), (1,null)不是唯一的?這背後的理由是什麼?

因爲沒有其他關鍵列來強制執行唯一性。

正如你所說,null不等於或不等於任何東西。如果您的唯一密鑰僅在col1之間,並且您有兩行設置爲空(允許),那麼查詢where col1 is null將會找到兩者 - 這是可以的,因爲is null不是關於相等性。你可以說兩行都符合條件,但不是它們等於空。與您的雙列鍵相同的將是where col1 = 1 and col2 is null。現在平等確實進場。

在這兩種情況下,空值都被忽略,剩下的任何東西都必須是唯一的。通過單列鍵,除了強制唯一性外,沒有別的東西。如果col2爲兩列鍵,那麼col1仍然需要進行比較,而且它本身必須是唯一的,纔能有效地進行比較。

你也允許這樣做:

INSERT INTO table1 VALUES (null, null); 
INSERT INTO table1 VALUES (null, null); 

同樣的道理也適用;他們的空值實際上被忽略了,但現在 - 與單列鍵一樣 - 沒有什麼可以執行唯一性了。

+0

我知道它正在做它應該做的事情。我試圖理解這種行爲的基本原理。更新我的問題。 – Santhosh

1

如果你真的想忽略空值和唯一阻止完整的複製鑰匙的插入,那麼你可以使用只能認爲是完全非空鍵基於函數的唯一索引的約束:

create unique index uniq_col1_col2 on table1 
    (case when col1 is not null and col2 is not null then col1 end 
    , case when col1 is not null and col2 is not null then col2 end 
);