如何建立一個也容許空值的惟一約束?

我想在要用GUID填充的列上具備惟一約束。 可是,個人數據包含此列的空值。 如何建立容許多個空值的約束? sql

這是一個示例方案 。 考慮如下模式: 數據庫

CREATE TABLE People (
  Id INT CONSTRAINT PK_MyTable PRIMARY KEY IDENTITY,
  Name NVARCHAR(250) NOT NULL,
  LibraryCardId UNIQUEIDENTIFIER NULL,
  CONSTRAINT UQ_People_LibraryCardId UNIQUE (LibraryCardId)
)

而後查看此代碼以瞭解我要實現的目標: 架構

-- This works fine:
INSERT INTO People (Name, LibraryCardId) 
 VALUES ('John Doe', 'AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA');

-- This also works fine, obviously:
INSERT INTO People (Name, LibraryCardId) 
VALUES ('Marie Doe', 'BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB');

-- This would *correctly* fail:
--INSERT INTO People (Name, LibraryCardId) 
--VALUES ('John Doe the Second', 'AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA');

-- This works fine this one first time:
INSERT INTO People (Name, LibraryCardId) 
VALUES ('Richard Roe', NULL);

-- THE PROBLEM: This fails even though I'd like to be able to do this:
INSERT INTO People (Name, LibraryCardId) 
VALUES ('Marcus Roe', NULL);

最後一條語句失敗,並顯示一條消息: app

違反UNIQUE KEY約束'UQ_People_LibraryCardId'。 沒法在對象「 dbo.People」中插入重複密鑰。 less

如何更改架構和/或惟一性約束,以便容許多個NULL值,同時仍檢查實際數據的惟一性? 測試


#1樓

您不能使用UNIQUE約束來執行此操做,可是能夠在觸發器中執行此操做。 this

CREATE TRIGGER [dbo].[OnInsertMyTableTrigger]
   ON  [dbo].[MyTable]
   INSTEAD OF INSERT
AS 
BEGIN
    SET NOCOUNT ON;

    DECLARE @Column1 INT;
    DECLARE @Column2 INT; -- allow nulls on this column

    SELECT @Column1=Column1, @Column2=Column2 FROM inserted;

    -- Check if an existing record already exists, if not allow the insert.
    IF NOT EXISTS(SELECT * FROM dbo.MyTable WHERE Column1=@Column1 AND Column2=@Column2 @Column2 IS NOT NULL)
    BEGIN
        INSERT INTO dbo.MyTable (Column1, Column2)
            SELECT @Column2, @Column2;
    END
    ELSE
    BEGIN
        RAISERROR('The unique constraint applies on Column1 %d, AND Column2 %d, unless Column2 is NULL.', 16, 1, @Column1, @Column2);
        ROLLBACK TRANSACTION;   
    END

END

#2樓

當我在下面應用惟一索引時: spa

CREATE UNIQUE NONCLUSTERED INDEX idx_badgeid_notnull
ON employee(badgeid)
WHERE badgeid IS NOT NULL;

每一個非null更新和插入均失敗,並顯示如下錯誤: code

UPDATE失敗,由於如下SET選項具備錯誤的設置:'ARITHABORT'。 server

我在MSDN上找到了

在計算列或索引視圖上建立或更改索引時,SET ARITHABORT必須爲ON。 若是SET ARITHABORT爲OFF,則對在計算列或索引視圖上具備索引的表執行CREATE,UPDATE,INSERT和DELETE語句將失敗。

所以,爲了使其正常工做,我這樣作了

右鍵單擊[數據庫]->屬性->選項->其餘選項->其餘->啓用算術停止-> true

我相信能夠在代碼中使用

ALTER DATABASE "DBNAME" SET ARITHABORT ON

但我尚未測試


#3樓

對於正在使用Microsoft SQL Server Manager並想要建立惟一但可爲空的索引的用戶,您能夠像一般那樣建立惟一索引,而後在新索引的索引屬性中,從左側面板中選擇「過濾器」,而後輸入您的過濾器(這是您的where子句)。 它應顯示以下內容:

([YourColumnName] IS NOT NULL)

這適用於MSSQL 2012


#4樓

如前所述,SQL Server在UNIQUE CONSTRAINT方面未實現ANSI標準。 自2007年以來,Microsoft Connect上已經有一張票。如此處和此處所述,到目前爲止,最好的選擇是使用另外一個答案中列出的過濾索引或計算列,例如:

CREATE TABLE [Orders] (
  [OrderId] INT IDENTITY(1,1) NOT NULL,
  [TrackingId] varchar(11) NULL,
  ...
  [ComputedUniqueTrackingId] AS (
      CASE WHEN [TrackingId] IS NULL
      THEN '#' + cast([OrderId] as varchar(12))
      ELSE [TrackingId_Unique] END
  ),
  CONSTRAINT [UQ_TrackingId] UNIQUE ([ComputedUniqueTrackingId])
)

#5樓

CREATE UNIQUE NONCLUSTERED INDEX [UIX_COLUMN_NAME]
ON [dbo].[Employee]([Username] ASC) WHERE ([Username] IS NOT NULL) 
WITH (ALLOW_PAGE_LOCKS = ON, ALLOW_ROW_LOCKS = ON, PAD_INDEX = OFF, SORT_IN_TEMPDB = OFF, 
DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, STATISTICS_NORECOMPUTE = OFF, ONLINE = OFF, 
MAXDOP = 0) ON [PRIMARY];
相關文章
相關標籤/搜索