我正在使用SQL Server 2005。
我有一个必须包含唯一值或NULL值的字段。我想我应该使用CHECK CONSTRAINT
或TRIGGER for INSERT, UPDATE
来强制执行此操作。
在触发器上使用约束是否有优势(反之亦然)?这样的约束/触发器可能是什么样的?
还是有其他更合适的选择,我没有考虑过吗?
答案 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)
答案 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已经过滤了索引(正如我输入的那样)