Sql 在具有循环外键的表上添加约束

Sql 在具有循环外键的表上添加约束,sql,sql-server,foreign-keys,constraints,Sql,Sql Server,Foreign Keys,Constraints,我们的系统中有用户,他们有联系人。因此,我们通过在contacts表中有一个外键来实现这一点,该外键指向users表中它所属的用户。但是现在我们需要添加一些功能,这些功能要求用户拥有一个默认联系人。因此,我为用户的默认联系人添加了一列,作为外键指向contacts表。请注意下表 这两个表(我已将它们切分为相关列): 请注意循环外键 现在我想向users表添加一个约束,该约束表示默认联系人只能是属于该用户的联系人(即联系人的用户id==将其作为默认联系人的用户的用户id)。我现在被卡住了,因为我不

我们的系统中有用户,他们有联系人。因此,我们通过在contacts表中有一个外键来实现这一点,该外键指向users表中它所属的用户。但是现在我们需要添加一些功能,这些功能要求用户拥有一个默认联系人。因此,我为用户的默认联系人添加了一列,作为外键指向contacts表。请注意下表

这两个表(我已将它们切分为相关列):

请注意循环外键

现在我想向users表添加一个约束,该约束表示默认联系人只能是属于该用户的联系人(即联系人的用户id==将其作为默认联系人的用户的用户id)。我现在被卡住了,因为我不确定如何添加此约束。这可能可以通过断言来解决,但由于我正在使用SqlServer,因此不支持断言。这一定是一个常见的问题,因为这是两天来我们第二次在两种完全不同的情况下遇到类似的情况

那么,甚至可以添加此约束吗?如果是这样,我将如何着手这样做


编辑--2014年1月15日03:39 UTC
所以我找到了一种方法来实现这一点,那就是通过在存储过程中检查来添加约束。只要我们总是通过存储过程访问users.default\u contact字段,这就行了,但我想知道是否还有其他严格的解决方案。

SQL Server
中,您可以使用多种技术来实现这一点。一种常见的方法是使用和的组合。这样,当您将约束添加到用户表时,每次执行
INSERT
UPDATE
语句时,约束都将验证为默认联系人指定的值

首先,您需要创建函数

create function ValidateContact(@userid int, @contact int)
returns bit
as
begin
   declare @count int,
           @retVal bit;

   select @count = count(*) from dbo.contacts where user_id = @userid and id = @contact

   if @count = 0 and @contact is not null
    set @retVal =  0;
    else
    set @retVal =  1;

   return @retVal;
end
go
此函数将接收两个参数(userid和contact),然后检查用户是否存在联系人。如果没有,则返回0(false),否则返回1(true)。您将需要创建
检查
约束

alter table dbo.users
add constraint chk_default_contact 
check (dbo.ValidateContact(user_id, default_contact) = 1)
go
现在,每次执行
INSERT
UPDATE
语句时,
CHECK
约束将根据
ValidateContact
函数中指定的逻辑验证输入,如果验证失败,将抛出错误并中止事务


希望能有所帮助

非常感谢,这绝对有效。我从未想过将函数集成到它中。我会在contacts表中设置一个
is\u default
列,然后实现一个约束,以确保每个
用户id只设置一个
。这很容易以声明的方式实现(通过过滤索引或索引视图),并避免堆积更多的工作(例如,您接受的函数不会阻止联系人更新其
user\u id
列以更正联系人所属的用户)。不,因为我实际上简化了它,实际上,我们有两个不同的默认值指向同一个表,但根据触点的类型,触点可以是两个或一个触点,因此这是不可行的。我们不想阻止联系人在不同的用户ID之间移动,而且管理联系人表中的默认列似乎比管理用户表中的默认列要多得多。
alter table dbo.users
add constraint chk_default_contact 
check (dbo.ValidateContact(user_id, default_contact) = 1)
go