2008-12-18 107 views
10

我正在使用SQL Server 2005.字段值必须是唯一的,除非为NULL

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

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

或者还有另一种更合适的选择,我没有考虑过?

+0

参见答案:http://stackoverflow.com/questions/191421/how-to-创建一个独特索引的空列 – 2010-02-25 10:01:09

回答

4

这是一个带约束做到这一点的另一种方式。为了执行这个约束,你需要一个函数来计算字段值的出现次数。在你的约束,只要确保这个最大是1

约束:

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

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

表函数返回一个int和使用以下选择导出它

declare @retVal int 

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

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

2

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

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

Here is some sample code.

+1

白皮书在哪里说这是一个好方法? 这个如果对于DB2但仍然适用:http://www.craigsmullins.com/viewnw.htm – gbn 2008-12-18 13:26:56

2

有没有在这个表上,也许标识列的主键?您可以创建一个唯一的密钥,该密钥是您正在实施唯一性的字段与主键相结合的组合。

有一个关于正是这种问题的讨论在这里:http://blog.sqlauthority.com/2008/09/07/sql-server-explanation-about-usage-of-unique-index-and-unique-constraint/

仅供参考 - SQL Server 2008中引入了过滤索引这将让你有点不同处理这个。

+0

我不知道是谁和为什么downvoted一个很好的解决方案。我喜欢它,因为它工作。 – 2009-11-18 19:28:29

+0

由于第一部分涉及组合键,因此可能拒绝了此答案。这将允许重复的空值,但它也会允许在应该包含唯一值的列中重复值。 – 2009-12-28 21:43:05

0

通常情况下,触发器允许您提供比检查约束更详细和更明确的信息,所以我使用这些信息来避免调试过程中的“哪列错误”游戏。

0

尽管唯一约束实际上是一个索引,但约束比触发器要轻得多。

但是,在唯一约束/索引中只允许有一个NULL。因此,你将不得不使用触发器来检测重复。

它已经requested from MS to ignore NULLS,但SQL 2008已经过滤索引(如提及,而我键入此)

6

我创建了一个视图,其索引通过where子句忽略了空值......即,如果向表中插入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及以上。

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 
相关问题