条件唯一约束
我有一种情况,我需要强制一组列的唯一约束,但只有一列的一个值。
所以例如我有一个像表(ID,名称,RecordStatus)表。
RecordStatus只能有一个值1或2(活动或删除),我想创build一个唯一的约束(ID,RecordStatus)只有当RecordStatus = 1,因为我不在乎是否有多个删除logging相同ID。
除了写触发器,我可以这样做吗?
我正在使用SQL Server 2005。
像这样添加一个检查约束。 不同之处在于,如果Status = 1且Count> 0,则返回false。
http://msdn.microsoft.com/en-us/library/ms188258.aspx
CREATE TABLE CheckConstraint ( Id TINYINT, Name VARCHAR(50), RecordStatus TINYINT ) GO CREATE FUNCTION CheckActiveCount( @Id INT ) RETURNS INT AS BEGIN DECLARE @ret INT; SELECT @ret = COUNT(*) FROM CheckConstraint WHERE Id = @Id AND RecordStatus = 1; RETURN @ret; END; GO ALTER TABLE CheckConstraint ADD CONSTRAINT CheckActiveCountConstraint CHECK (NOT (dbo.CheckActiveCount(Id) > 1 AND RecordStatus = 1)); INSERT INTO CheckConstraint VALUES (1, 'No Problems', 2); INSERT INTO CheckConstraint VALUES (1, 'No Problems', 2); INSERT INTO CheckConstraint VALUES (1, 'No Problems', 2); INSERT INTO CheckConstraint VALUES (1, 'No Problems', 1); INSERT INTO CheckConstraint VALUES (2, 'Oh no!', 1); INSERT INTO CheckConstraint VALUES (2, 'Oh no!', 2); -- Msg 547, Level 16, State 0, Line 14 -- The INSERT statement conflicted with the CHECK constraint "CheckActiveCountConstraint". The conflict occurred in database "TestSchema", table "dbo.CheckConstraint". INSERT INTO CheckConstraint VALUES (2, 'Oh no!', 1); SELECT * FROM CheckConstraint; -- Id Name RecordStatus -- ---- ------------ ------------ -- 1 No Problems 2 -- 1 No Problems 2 -- 1 No Problems 2 -- 1 No Problems 1 -- 2 Oh no! 1 -- 2 Oh no! 2 ALTER TABLE CheckConstraint DROP CONSTRAINT CheckActiveCountConstraint; DROP FUNCTION CheckActiveCount; DROP TABLE CheckConstraint;
看哪, 过滤的索引 。 从文档(重点是我的):
过滤后的索引是一个优化的非聚簇索引,特别适用于涵盖从定义良好的数据子集中进行select的查询。 它使用filter谓词来索引表中的一部分行。 与全表索引相比,精心devise的过滤索引可以提高查询性能,并减less索引维护和存储成本。
下面是一个将唯一索引与filter谓词相结合的示例:
create unique index [MyIndex] on [MyTable]([ID]) where [RecordStatus] = 1
当RecordStatus
为1
时,这基本上强制了ID
唯一性。
注意:筛选索引是在SQL Server 2008中引入的。对于早期版本的SQL Server,请参阅此答案 。
您可以将删除的logging移动到缺less约束的表中,也可以使用两个表的UNION视图来保留单个表的外观。
你可以用一种非常冒险的方式来做到这一点
在您的表上创build一个模式绑定视图。
创build视图无论SELECT * FROM表WHERE RecordStatus = 1
现在用你想要的字段在视图上创build一个唯一的约束。
关于模式化视图的一个注意事项虽然,如果你改变基础表,你将不得不重新创build视图。 因此,大量的陷阱。
因为,你将允许重复,一个独特的约束将无法正常工作。 您可以为RecordStatus列和INSERT的存储过程创build检查约束,在插入重复ID之前检查现有的活动logging。
如果您不能像Bill所build议的那样使用NULL作为RecordStatus,那么可以将他的想法和基于函数的索引结合起来。 创build一个返回NULL的函数,如果RecordStatus不是你想在约束中考虑的值(否则RecordStatus),并创build一个索引。
这样做的好处是不必在约束中显式检查表中的其他行,这可能会导致性能问题。
我应该说我根本不知道SQL服务器,但是我已经在Oracle中成功地使用了这种方法。