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
有没有办法用表约束来做到这一点?
UPDATE
我现在已经接受了Martin Smith的回答,尽管我将在即将发布的版本中推动JohnFx的重构,因为它是最好的长期解决方案.但是我想根据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;
变化:
>使用@tempTable而不是
根据udf的要求,tempTable(在内存中,写入磁盘)
>传递@ fk1作为参数,这样我就可以在一个中选择唯一性
一组fk1值.
>棘手也必须通过isPrimary,即使它不是
必要的逻辑
功能,否则sql2005
优化器不会运行检查
isPrimary时的约束
更新.
解决方法
在检查约束中使用UDF可能会在
snapshot isolation或
multirow updates下失败.
假设您的所有fk1和pk1值当前(并且将始终为)为正,您可以使用以下定义创建计算列
CASE WHEN isPrimary = 1 THEN fk1 ELSE -pk1 END
然后添加一个唯一的约束.或者,如果不能做出那个假设那么可能
CASE WHEN isPrimary = 0 THEN 1.0/pk1 ELSE fk1 END