SQL Server如何保證可空字段中非空值惟一

今天同窗向我提了一個問題,我以爲蠻有意思,現記錄下來你們探討下。 問題是:在一個表裏面,有一個容許爲空的字段,空是能夠重複的,可是不爲空的值須要惟一。 表結構以下面代碼建立 CREATE TABLE test_tb (     TestId int not null identity(1,1) primary key,     Caption nvarchar(100) null ); GO 解決方案1: 對於這個問題,你們的第一個想法多是:在Caption這個字段上面加一個惟一鍵不就能夠了嗎?好,咱們按着這個思路作下去,先建立惟一索引。 CREATE UNIQUE NONCLUSTERED INDEX un_test_tb  ON test_tb(Caption) GO 索引建立好了,咱們來測試下效果 INSERT INTO test_tb (Caption) VALUES (null) GO INSERT INTO test_tb (Caption) VALUES (null) GO 運行以後咱們會收到下面的錯誤信息: 消息 2601,級別 14,狀態 1,第 1 行 不能在具備惟一索引 'un_test_tb' 的對象 'dbo.test_tb' 中插入重複鍵的行。 語句已終止。 因此該解決方案是不行的。 解決方案2: 添加約束,讓SQL Server在插入數據的時候,先驗證下已有數據中是否有如今要插入的這個值。因爲這個約束不是簡單的一個運算,所以咱們先建立一個函數,而後再在約束中調用這個函數。 建立驗證邏輯函數: CREATE FUNCTION [dbo].[fn_CK_test_tb_Caption]() RETURNS BIT AS  BEGIN     IF(EXISTS(     SELECT    1     FROM test_tb AS a     WHERE (Caption IS NOT NULL) AND EXISTS       (SELECT 1 AS Expr1         FROM test_tb         WHERE (Caption IS NOT NULL) AND (Caption = a.Caption) AND (a.TestId <> TestId))      ))         RETURN 0          RETURN 1 END GO 在約束中引用函數: ALTER TABLE test_tb ADD CONSTRAINT CK_test_tb_Caption CHECK (dbo.fn_CK_test_tb_Caption() = 1) GO 如今來測試下效果。先來測試NULL值 INSERT INTO test_tb (Caption) VALUES (null) GO INSERT INTO test_tb (Caption) VALUES (null) GO SELECT * FROM test_tb GO 能夠成功運行,並且也出了多行爲NULL的狀況。如今再來測試不爲空的插入狀況。 INSERT INTO test_tb (Caption) VALUES (N'AAA') GO INSERT INTO test_tb (Caption) VALUES (N'BBB') GO INSERT INTO test_tb (Caption) VALUES (N'BBB') GO SELECT * FROM test_tb GO 結果是在第三條語句的時候報錯了,表中的Caption字段也有'AAA'和'BBB'了,這也正好是咱們要的結果。 因此解決方案2是正確的。可是爲了這麼一個小小功能,就寫這麼長一段東西是否是太繁瑣了呢?咱們來看下面的解決方案。 解決方案3:(只適用於SQL Server 2008) SQL Server 2008中有了一個優雅的解決方案,那就是篩選索引。篩選索引是一種通過優化的非彙集索引,尤爲適用於涵蓋從定義完善的數據子集中選擇數據的查詢。篩選索引使用篩選謂詞對錶中的部分行進行索引。有了篩選索引,咱們只須要寫一條語句就達到上面的效果。 CREATE UNIQUE NONCLUSTERED INDEX un_test_tb  ON test_tb(Caption) WHERE Caption is not null GO 再用上面的一些測試語句來測試的話,會發現徹底是達到了咱們的要求。 這個方案的惟一缺點就是該語句只有SQL Server 2008支持。。 不知道各位有沒有又優雅又適用於各個版本的SQL Server的解決方案,望不勝賜教。 
相關文章
相關標籤/搜索