新聞中心
今天同學(xué)向我提了一個(gè)問(wèn)題,是SQL Server中一個(gè)關(guān)于“如何保證可空字段中非空值唯一”的問(wèn)題,我覺(jué)得蠻有意思,現(xiàn)記錄下來(lái)大家探討下。

成都創(chuàng)新互聯(lián)公司是專業(yè)的麥蓋提網(wǎng)站建設(shè)公司,麥蓋提接單;提供網(wǎng)站設(shè)計(jì)制作、成都網(wǎng)站制作,網(wǎng)頁(yè)設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行麥蓋提網(wǎng)站開(kāi)發(fā)網(wǎng)頁(yè)制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛(ài)的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來(lái)合作!
問(wèn)題是:在一個(gè)表里面,有一個(gè)允許為空的字段,空是可以重復(fù)的,但是不為空的值需要唯一。
表結(jié)構(gòu)如下面代碼創(chuàng)建
- CREATE TABLE test_tb
- (
- TestId int not null identity(1,1) primary key,
- Caption nvarchar(100) null
- );
- GO
#p#
解決方案:
解決方案1:
對(duì)于這個(gè)問(wèn)題,大家的第一個(gè)想法可能是:在Caption這個(gè)字段上面加一個(gè)唯一鍵不就可以了嗎?好,我們按著這個(gè)思路做下去,先創(chuàng)建唯一索引。
- CREATE UNIQUE NONCLUSTERED INDEX un_test_tb
- ON test_tb(Caption)
- GO
索引創(chuàng)建好了,我們來(lái)測(cè)試下效果
- INSERT INTO test_tb (Caption)
- VALUES (null)
- GO
- INSERT INTO test_tb (Caption)
- VALUES (null)
- GO
運(yùn)行之后我們會(huì)收到下面的錯(cuò)誤信息:
- 消息 2601,級(jí)別 14,狀態(tài) 1,第 1 行
- 不能在具有唯一索引 'un_test_tb' 的對(duì)象 'dbo.test_tb' 中插入重復(fù)鍵的行。
- 語(yǔ)句已終止。
所以該解決方案是不行的。
解決方案2:
添加約束,讓SQL Server在插入數(shù)據(jù)的時(shí)候,先驗(yàn)證下已有數(shù)據(jù)中是否有現(xiàn)在要插入的這個(gè)值。由于這個(gè)約束不是簡(jiǎn)單的一個(gè)運(yùn)算,因此我們先創(chuàng)建一個(gè)函數(shù),然后再在約束中調(diào)用這個(gè)函數(shù)。
創(chuàng)建驗(yàn)證邏輯函數(shù):
- 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
在約束中引用函數(shù):
- ALTER TABLE test_tb
- ADD CONSTRAINT CK_test_tb_Caption CHECK (dbo.fn_CK_test_tb_Caption() = 1)
- GO
現(xiàn)在來(lái)測(cè)試下效果。先來(lái)測(cè)試NULL值
- INSERT INTO test_tb (Caption)
- VALUES (null)
- GO
- INSERT INTO test_tb (Caption)
- VALUES (null)
- GO
- SELECT * FROM test_tb
- GO
可以成功運(yùn)行,而且也出了多行為NULL的情況?,F(xiàn)在再來(lái)測(cè)試不為空的插入情況。
- 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
結(jié)果是在第三條語(yǔ)句的時(shí)候報(bào)錯(cuò)了,表中的Caption字段也有'AAA'和'BBB'了,這也正好是我們要的結(jié)果。
所以解決方案2是正確的。但是為了這么一個(gè)小小功能,就寫(xiě)這么長(zhǎng)一段東西是不是太繁瑣了呢?我們來(lái)看下面的解決方案。
解決方案3:(只適用于SQL Server 2008)
SQL Server 2008中有了一個(gè)優(yōu)雅的解決方案,那就是篩選索引。篩選索引是一種經(jīng)過(guò)優(yōu)化的非聚集索引,尤其適用于涵蓋從定義完善的數(shù)據(jù)子集中選擇數(shù)據(jù)的查詢。篩選索引使用篩選謂詞對(duì)表中的部分行進(jìn)行索引。有了篩選索引,我們只需要寫(xiě)一條語(yǔ)句就達(dá)到上面的效果。
- CREATE UNIQUE NONCLUSTERED INDEX un_test_tb
- ON test_tb(Caption)
- WHERE Caption is not null
- GO
再用上面的一些測(cè)試語(yǔ)句來(lái)測(cè)試的話,會(huì)發(fā)現(xiàn)完全是達(dá)到了我們的要求。
這個(gè)方案的唯一缺點(diǎn)就是該語(yǔ)句只有SQL Server 2008支持。
不知道各位有沒(méi)有又優(yōu)雅又適用于各個(gè)版本的SQL Server的解決方案,望不勝賜教。
新聞標(biāo)題:SQLServer如何保證可空字段中非空值唯一
網(wǎng)頁(yè)路徑:http://www.fisionsoft.com.cn/article/dhopgdc.html


咨詢
建站咨詢
