2008-12-18 150 views
10

我正在使用SQL Server 2005.字段值必須是唯一的,除非爲NULL

我有一個必須包含唯一值或NULL值的字段。我想我應該通過CHECK CONSTRAINTTRIGGER for INSERT, UPDATE執行此操作。

在觸發器上使用約束是否有優勢(反之亦然)?什麼可能這樣的約束/觸發器看起來像?

或者還有另一種更合適的選擇,我沒有考慮過?

+0

參見答案:http://stackoverflow.com/questions/191421/how-to-創建一個獨特索引的空列 – 2010-02-25 10:01:09

回答

4

這是一個帶約束做到這一點的另一種方式。爲了執行這個約束,你需要一個函數來計算字段值的出現次數。在你的約束,只要確保這個最大是1

約束:

field is null or dbo.fn_count_maximum_of_field(field) < 2 

編輯我不記得現在 - 並不能檢查,要麼 - 無論是約束檢查在插入/更新之前或之後完成。我想在插入/更新失敗後回滾。如果事實證明我錯了,上面的2應該是1

表函數返回一個int和使用以下選擇導出它

declare @retVal int 

    select @retVal = max(occurrences) 
    from ( 
     select field, count(*) as occurrences 
     from dbo.tbl 
     where field = @field 
     group by field 
    ) tmp 

如果你的列這應該是相當快的作爲(非唯一)索引。

2

在Oracle中,唯一鍵將允許多個NULL。

在SQL Server 2005中,一個好方法是通過視圖進行插入,並禁用直接插入到表中。

Here is some sample code.

+1

白皮書在哪裏說這是一個好方法? 這個如果對於DB2但仍然適用:http://www.craigsmullins.com/viewnw.htm – gbn 2008-12-18 13:26:56

2

有沒有在這個表上,也許標識列的主鍵?您可以創建一個唯一的密鑰,該密鑰是您正在實施唯一性的字段與主鍵相結合的組合。

有一個關於正是這種問題的討論在這裏:http://blog.sqlauthority.com/2008/09/07/sql-server-explanation-about-usage-of-unique-index-and-unique-constraint/

僅供參考 - SQL Server 2008中引入了過濾索引這將讓你有點不同處理這個。

+0

我不知道是誰和爲什麼downvoted一個很好的解決方案。我喜歡它,因爲它工作。 – 2009-11-18 19:28:29

+0

由於第一部分涉及組合鍵,因此可能拒絕了此答案。這將允許重複的空值,但它也會允許在應該包含唯一值的列中重複值。 – 2009-12-28 21:43:05

0

通常情況下,觸發器允許您提供比檢查約束更詳細和更明確的信息,所以我使用這些信息來避免調試過程中的「哪列錯誤」遊戲。

0

儘管唯一約束實際上是一個索引,但約束比觸發器要輕得多。

但是,在唯一約束/索引中只允許有一個NULL。因此,你將不得不使用觸發器來檢測重複。

它已經requested from MS to ignore NULLS,但SQL 2008已經過濾索引(如提及,而我鍵入此)

6

我創建了一個視圖,其索引通過where子句忽略了空值......即,如果向表中插入null,視圖不關心,但如果插入非空值,視圖將強制約束。

create view dbo.UniqueAssetTag with schemabinding 
as 
select asset_tag 
from dbo.equipment 
where asset_tag is not null 

GO 

create unique clustered index ix_UniqueAssetTag 
on UniqueAssetTag(asset_tag) 

GO 

所以現在我的設備表有一個asset_tag列,允許多個空值但只有唯一的非空值。

注意: 如果使用mssql 2000,在對錶執行任何插入,更新或刪除操作之前,您需要「SET ARITHABORT ON」。很確定這是不需要在MSSQL 2005及以上。

3

您可以通過創建一個計算列並將該唯一索引放在該列上來完成此操作。

ALTER TABLE MYTABLE 
ADD COL2 AS (CASE WHEN COL1 IS NULL THEN CAST(ID AS NVARCHAR(255)) ELSE COL1 END) 

CREATE UNIQUE INDEX UQ_COL2 ON MYTABLE (COL2) 

這是假設ID是您的表的PK,而COL1是「唯一或空值」列。

如果您的「唯一」列爲空,計算列(COL2)將使用PK的值。

還有ID列並COL1在下面的例子中之間衝突的可能性:

ID  COL1 COL2 
1  [NULL] 1 
2  1  1 

爲了解決這個問題,我通常創建其存儲另一計算列在COL2值是否來自ID列或COL1柱:

ALTER TABLE MYTABLE 
ADD COL3 AS (CASE WHEN COL1 IS NULL THEN 1 ELSE 0 END) 

索引應改爲:

CREATE UNIQUE INDEX UQ_COL2 ON MYTABLE (COL2, COL3) 

現在指數是在兩個計算列COL2和COL3所以沒有問題:提供對這個問題

ID  COL1 COL2 COL3 
1  [NULL] 1  1 
2  1  1  0 
相關問題