如何建立一个也容许空值的惟一约束?

我想在要用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];
相关文章
相关标签/搜索