在两个数据库之间添加外键关系


90

我在两个不同的数据库中有两个表。在table1中(在database1中)有一个称为column1的列,它是主键。现在在table2中(在database2中)有一个名为column2的列,我想将其添加为外键。

我尝试添加它,它给了我以下错误:

消息1763,级别16,状态0,第1行
不支持跨数据库外键引用。外键Database2.table2。

消息1750,级别16,状态0,第1行
无法创建约束。请参阅先前的错误。

由于表位于不同的数据库中,我该怎么办。

Answers:


84

您将需要使用触发器来管理跨数据库的引用约束。


基本上,您将创建一个插入,更新触发器以验证主键表中是否存在该键。如果密钥不存在,则还原插入或更新,然后处理异常。

例:

Create Trigger dbo.MyTableTrigger ON dbo.MyTable, After Insert, Update
As
Begin

   If NOT Exists(select PK from OtherDB.dbo.TableName where PK in (Select FK from inserted) BEGIN
      -- Handle the Referential Error Here
   END

END

编辑: 只是为了澄清。这不是强制执行参照完整性的最佳方法。理想情况下,您希望将两个表都放在同一个数据库中,但是那样是不可能的。那么以上是您可能需要解决的问题。


3
@John Hartsock-上面的示例很容易失败,而无需添加适当的事务处理。中能与发生问题的类型体面的讨论,“如果不存在(),然后插入”可以在这里找到- stackoverflow.com/questions/108403/...
EBarr

16
@John Hartsock-您的解决方案存在漏洞:如果从备份中还原两个数据库之一,则触发器当然不会触发。这样我们才能以孤立行结束。
AK

4
@AlexKuznetsov确实如此。正如我所解释的,这不是最佳方法,而是可能的解决方法。
John Hartsock 2011年

2
这仅仅是错的......我只希望OP意识到刚才的事实,他要求的这样的事情,是一种症状,他是最有可能做错了什么......更不用说想想触发器..
MeTitus

1
@Marco正如我在回答中发布的“只是要澄清。这不是强制执行引用完整性的最佳方法。理想情况下,您希望两个表都位于同一个db中,但如果这不可能,那么上面的方法可能会解决您。” 我解释说这可能不是一个好主意。
约翰·哈索克

48

如果需要坚如磐石的完整性,请将两个表都放在一个数据库中,并使用FK约束。如果您的父表位于另一个数据库中,则任何措施都无法阻止任何人从旧的备份中还原该父数据库,然后您将成为孤儿。

这就是为什么不支持数据库之间的FK。


27

以我的经验,当两个相关表的主要权威信息源必须位于两个单独的数据库中时,处理此问题的最佳方法是将表的副本从主要位置同步到辅助位置(使用T-具有适当错误检查的SQL或SSIS-当表具有外键引用时,您无法截断并重新填充表,因此有几种方法可以在更新表时使用猫。

然后将第二个位置的传统FK关系添加到表中,该关系实际上是只读副本。

您可以在主要位置使用触发器或计划作业,以使副本保持更新。


1
回覆。“您可以在主要位置触发或安排作业以保持副本更新”:为什么不只使用SQL Server复制(特别是事务与合并类型,因为订阅者的副本(具有需要外键约束的表的副本)只是需要为只读)?请参阅:链接
Tom

@Tom是的,您当然可以使用复制在远程数据库中保持表副本的更新。
Cade Roux

20

您可以将检查约束与用户定义的函数配合使用来进行检查。它比触发器更可靠。可以在必要时与外键一样禁用和重新启用它,并在database2恢复后重新检查它。

CREATE FUNCTION dbo.fn_db2_schema2_tb_A
(@column1 INT) 
RETURNS BIT
AS
BEGIN
    DECLARE @exists bit = 0
    IF EXISTS (
      SELECT TOP 1 1 FROM DB2.SCHEMA2.tb_A 
      WHERE COLUMN_KEY_1 =  @COLUMN1
    ) BEGIN 
         SET @exists = 1 
      END;
      RETURN @exists
END
GO

ALTER TABLE db1.schema1.tb_S
  ADD CONSTRAINT CHK_S_key_col1_in_db2_schema2_tb_A
    CHECK(dbo.fn_db2_schema2_tb_A(key_col1) = 1)

1
这是比接受的答案更好的解决方案,您也可以在多个表上重复使用它
Milox

3

简短的答案是,SQL Server(从SQL 2008开始)不支持跨数据库外键,这是错误消息指出的。

虽然您不能具有声明性参照完整性(FK),但是可以使用触发器来达到相同的目标。它的可靠性稍差一些,因为您编写的逻辑可能存在错误,但可以使您保持一致。

请参阅SQL文档@ http://msdn.microsoft.com/zh-cn/library/aa258254%28v=sql.80%29.aspx哪个状态:

触发器通常用于执行业务规则和数据完整性。SQL Server通过表创建语句(ALTER TABLE和CREATE TABLE)提供声明性引用完整性(DRI)。但是,DRI不提供跨数据库引用完整性。要强制执行引用完整性(关于表的主键和外键之间的关系的规则),请使用主键和外键约束(ALTER TABLE和CREATE TABLE的PRIMARY KEY和FOREIGN KEY关键字)。如果触发器表上存在约束,则在INSTEAD OF触发器执行之后和AFTER触发器执行之前检查约束。如果违反了约束,则会回退INSTEAD OF触发器动作,并且不会执行(触发)AFTER触发器。

在SQLTeam上也可以进行讨论-http://www.sqlteam.com/forums/topic.asp?TOPIC_ID = 31135


0

如错误消息所述,SQL Server不支持此功能。确保基准完整性的唯一方法是使用触发器。


1
您能用一个例子来解释一下吗
山姆(Sam)2010年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.