DBMS: MS Sql Server 2005,标准版
我想对一个表进行约束,使其只有一个记录在表的子集内具有特定的值(其中行在特定列中共享一个值)。这可能吗?
示例: 我在myTable中有一个记录,该记录具有一个非唯一的外键(fk1),以及一个称为isPrimary的位列,以指出此特定记录应由我们的应用程序用于特殊逻辑。
在摘要中,它看起来像这样:
myTable ------------- pk1 (int, not null) name (varchar(50), null) fk1 (int, not null) isPrimary (bit, not null)
我想确保对于fk1的每个唯一值, 只有一个 记录的isPrimary标志设置为1。
数据示例:这应该合法:
pk1 name fk1 isPrimary ---- ----- ----- ---------- 1 Bill 111 1 2 Tom 111 0 3 Dick 222 1 4 Harry 222 0
但这 不 应该是(fk = 111时不止一个):
pk1 name fk1 isPrimary ---- ----- ----- ---------- 1 Bill 111 1 2 Tom 111 1 3 Dick 222 1 4 Harry 222 0
而且也不应该这样(在fk = 222时不存在):
pk1 name fk1 isPrimary ---- ----- ----- ---------- 1 Bill 111 1 2 Tom 111 0 3 Dick 222 0 4 Harry 222 0
有没有办法用表约束做到这一点?
更新 尽管我将在即将发布的版本中推动JohnFx的重构,但我现在已经接受了Martin Smith的回答,因为这是最好的长期解决方案。但是,我想根据Raze2dust的答案发布更新的UDF,以防将来的读者认为这更适合他们的需求。
CREATE FUNCTION [dbo].[OneIsPrimaryPerFK1](@fk1 INT, @dummyIsPrimary BIT) RETURNS INT AS BEGIN DECLARE @retval INT; DECLARE @primarySum INT; SET @retval = 0; DECLARE @TempTable TABLE ( fk1 INT, PrimarySum INT) INSERT INTO @TempTable SELECT fk1, SUM(CAST(isPrimary AS INT)) AS PrimarySum FROM FacAdmin WHERE fk1 = @fk1 GROUP BY fk1; SELECT @primarySum = PrimarySum FROM @TempTable; IF(@primarySum=1) BEGIN SET @retval = 1 END RETURN @retval END;
变化:
在快照约束或多行更新下,在检查约束中使用UDF可能会失败。
假设所有的fk1和pk1值当前都是(并且将始终是)正值,则可以使用以下定义创建一个计算列
CASE WHEN isPrimary = 1 THEN fk1 ELSE -pk1 END
然后为其添加唯一约束。或者,如果无法做出该假设,那么也许
CASE WHEN isPrimary = 0 THEN 1.0/pk1 ELSE fk1 END