Sql 某个值相同的记录组中的唯一约束
DBMS:MS Sql Server 2005,标准版 我想做一个表约束,使只有一条记录在表的子集中具有特定值(其中行在特定列中共享一个值)。这可能吗 示例: 我在myTable中有一个记录,它有一个非唯一外键(fk1),还有一个名为isPrimary的位列,用来指出我们的应用程序应该使用这个特殊的记录来实现特殊的逻辑 抽象地说,它是这样的:Sql 某个值相同的记录组中的唯一约束,sql,sql-server,database,sql-server-2005,constraints,Sql,Sql Server,Database,Sql Server 2005,Constraints,DBMS:MS Sql Server 2005,标准版 我想做一个表约束,使只有一条记录在表的子集中具有特定值(其中行在特定列中共享一个值)。这可能吗 示例: 我在myTable中有一个记录,它有一个非唯一外键(fk1),还有一个名为isPrimary的位列,用来指出我们的应用程序应该使用这个特殊的记录来实现特殊的逻辑 抽象地说,它是这样的: myTable ------------- pk1 (int, not null) name (varchar(50), null)
myTable
-------------
pk1 (int, not null)
name (varchar(50), null)
fk1 (int, not null)
isPrimary (bit, not null)
我想确保对于fk1的每个唯一值,都有一条且只有一条iPrimary标志设置为1的记录
数据示例:
这应该是合法的:
pk1 name fk1 isPrimary
---- ----- ----- ----------
1 Bill 111 1
2 Tom 111 0
3 Dick 222 1
4 Harry 222 0
但这应该而不是(fk=111时不止一个):
这也不应该(fk=222时无):
有没有一种方法可以通过表约束实现这一点
更新
我现在同意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;
变化:
SQL 2005不提供将Where子句应用于唯一索引(如SQL 2008)的功能。但是,有几种方法可以解决SQL 2005中的问题:
您可以尝试创建函数,然后使用检查约束:
CREATE FUNCTION ChkFn()
RETURNS INT
AS
BEGIN
DECLARE @retval INT
DECLARE @distinct INT
DECLARE @top INT
SET @retval = 0
SELECT fk1 AS ForeignKey, SUM(isPrimary) AS PrimarySum
INTO #TempTable
FROM myTable
GROUP BY fk1
SELECT @distinct = COUNT(DISTINCT(PrimarySum)) FROM #TempTable
SELECT @top = top PrimarySum FROM #TempTable
IF(@distinct=1 AND @top=1)
BEGIN
@retval = 1
END
RETURN @retval
END;
GO
ALTER TABLE myTable
ADD CONSTRAINT chkFkPk CHECK (dbo.ChekFn() = 1 );
GO
试试看,让我知道它是否有效。虽然不是很优雅。开始了一个新的答案,因为我把第一个答案弄坏了 听起来,您可以通过稍微重新考虑一下表设计来解决这个问题,以避免在实现业务规则时强行施加约束 从MyTable中删除IsPrimary列,然后将PrimaryPersonID列添加到引用primary person的另一个表中,怎么样
这样,结构本身将强制每个人在FK表中只有一个条目是主条目 在检查约束中使用UDF可能在或下失败 假设所有fk1和pk1值当前(并且将始终)为正值,则可以使用以下定义创建计算列
CASE WHEN isPrimary = 1 THEN fk1 ELSE -pk1 END
然后添加一个唯一的约束。或者如果这个假设不能成立,那么也许
CASE WHEN isPrimary = 0 THEN 1.0/pk1 ELSE fk1 END
4.创建一个在检查约束中使用的存储函数警告:约束中的udf可以工作,但如果它执行SELECT操作,也会导致严重的性能问题。性能损失只会发生在插入和更新时,对吗?我无法想象在选择数据时会触发检查约束。如果这是真的,我没问题,因为表不是经常写入的。你完全正确,这应该是数据模型,我希望我们可以实现它。目前,我们有一些不同的应用程序指向这一点,我们现在无法将它们全部更改以访问更新的模型。我将在未来推动这一改变。这非常接近,让我朝着正确的方向前进,所以我将把它标记为接受。我将在周一发布完成的函数。第一个问题是你不能在UDF中使用#tentrables,你必须使用@tentrable。另一个我会在更新后的帖子中提到。啊,是的,我忘记了UDF。。。不知道多行更新/快照隔离。谢谢你的文章。。我会试着从中理解。我是一个新手:-)虽然JohnFx的解决方案是最好的长期解决方案,但这是我现在的解决方案。
CASE WHEN isPrimary = 1 THEN fk1 ELSE -pk1 END
CASE WHEN isPrimary = 0 THEN 1.0/pk1 ELSE fk1 END