字段值必须是唯一的,除非它为NULL

时间:2008-12-18 13:14:23

标签: sql sql-server sql-server-2005 null

我正在使用SQL Server 2005。

我有一个必须包含唯一值或NULL值的字段。我想我应该使用CHECK CONSTRAINTTRIGGER for INSERT, UPDATE来强制执行此操作。

在触发器上使用约束是否有优势(反之亦然)?这样的约束/触发器可能是什么样的?

还是有其他更合适的选择,我没有考虑过吗?

7 个答案:

答案 0 :(得分:6)

我创建了一个带有索引的视图,该索引通过where子句忽略空值...即。如果在表中插入null,则视图不关心,但如果插入非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及以上版本不需要这样做。

答案 1 :(得分:4)

这是使用约束执行此操作的另一种方法。为了强制执行此约束,您需要一个计算字段值出现次数的函数。在约束中,只需确保此最大值为1。

约束:

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

编辑我现在不记得 - 也无法检查 - 是否在插入/更新之前或之后完成约束检查。我认为插入/更新在失败后回滚。如果事实证明我错了,上面的2应该是1。

表函数返回一个int并使用以下select来派生它

   declare @retVal int

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

如果您的列作为(非唯一)索引,这应该相当快。

答案 2 :(得分: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

答案 3 :(得分:2)

在Oracle中,唯一键将允许多个NULL。

在SQL Server 2005中,一种好方法是通过视图进行插入,并禁用直接插入表中。

Here is some sample code.

答案 4 :(得分:2)

此表上是否有主键,可能是Identity列?您可以创建一个唯一键,该键是与主键一起强制执行唯一性的字段的组合。

这里有关于此类问题的讨论:http://blog.sqlauthority.com/2008/09/07/sql-server-explanation-about-usage-of-unique-index-and-unique-constraint/

仅供参考 - SQL Server 2008引入了过滤索引,使您可以稍微采用不同的方法。

答案 5 :(得分:0)

通常,触发器将允许您提供比检查约束更详细和解释性的消息,因此我已经使用它们来避免调试中的“哪一列是坏的”游戏。

答案 6 :(得分:0)

约束比触发器轻得多,即使唯一约束实际上是索引。

但是,在唯一约束/索引中只允许一个NULL。 因此,您必须使用触发器来检测重复项。

它是requested from MS to ignore NULLS,但是SQL 2008已经过滤了索引(正如我输入的那样)