我有一种情况,我需要对一组列强制执行唯一约束,但仅对列的一个值执行。
因此,例如,我有一个像Table(ID,Name,RecordStatus)的表。
RecordStatus只能具有值1或2(有效或已删除),并且我只想在RecordStatus = 1时才对(ID,RecordStatus)创建唯一约束,因为我不在乎是否有多个删除的记录具有相同的ID。
除了编写触发器,我还能这样做吗?
我正在使用SQL Server 2005。
我有一种情况,我需要对一组列强制执行唯一约束,但仅对列的一个值执行。
因此,例如,我有一个像Table(ID,Name,RecordStatus)的表。
RecordStatus只能具有值1或2(有效或已删除),并且我只想在RecordStatus = 1时才对(ID,RecordStatus)创建唯一约束,因为我不在乎是否有多个删除的记录具有相同的ID。
除了编写触发器,我还能这样做吗?
我正在使用SQL Server 2005。
Answers:
添加这样的检查约束。区别在于,如果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;
看,过滤后的索引。从文档(重点是我的):
过滤索引是一种优化的非聚集索引,特别适合覆盖从定义明确的数据子集中选择的查询。它使用过滤谓词索引表中部分行。与全表索引相比,设计良好的筛选索引可以提高查询性能,并减少索引维护和存储成本。
这是结合唯一索引和过滤谓词的示例:
create unique index MyIndex
on MyTable(ID)
where RecordStatus = 1;
这从本质上强制了ID
when RecordStatus
is的唯一性1
。
创建该索引后,违反唯一性将引发错误:
消息2601,级别14,状态1,第13
行无法在具有唯一索引“ MyIndex”的对象“ dbo.MyTable”中插入重复的键行。重复的密钥值为(9999)。
注意:筛选后的索引是SQL Server 2008中引入的。有关SQL Server的早期版本,请参见此答案。
ansi_padding
过滤索引,因此请确保SET ANSI_PADDING ON
在创建过滤索引之前执行该选项以将其打开。
您可以将已删除的记录移动到没有约束的表中,并可能使用两个表具有UNION的视图来保留单个表的外观。
您可以以一种非常怪异的方式来做到这一点...
在表上创建一个架构绑定的视图。
从SELECT WHERE RecordStatus = 1的表创建任何内容
现在,在具有所需字段的视图上创建唯一约束。
但是,有一个关于架构绑定视图的说明,如果您更改基础表,则必须重新创建视图。因此有很多陷阱。