圆形外键。我该如何处理它们?

时间:2010-05-19 09:23:56

标签: .net sql sql-server foreign-keys

我的大部分sql代码都是生成的(来自POD)。现在我有问题,表user_data有一个FK到ref_status,它指向两个不同的user_data。我的代码执行以下操作

  1. 开始交易
  2. 查看user_data(并将其添加到列表中)
  3. 看到ref_status然后用它重复#2
  4. 执行create table ref_status code
  5. 然后我得到了例外

    Foreign key 'FK__...__0F975522' references invalid table 'user_data'.
    

    如果他们同时使用彼此作为参考,我如何创建两个表?我认为,因为它是在同一个交易中,它会起作用。我还要注意这个代码在sqlite中工作正常,启用了FK支持(自上个月发布的System.Data.SQLite以来一直支持)。那么我期望如何创建这两个表呢?

2 个答案:

答案 0 :(得分:6)

SQL Server中不支持循环外键。如果你真的想要它可以做到,但它没有用,因为你无法插入任何数据 - 你不能插入表A,因为表B中所需的引用不存在,反之亦然。唯一的方法是创建一个没有FK的表,然后在创建第二个表后添加它。然后,要插入数据,您需要禁用其中一个FK,然后重新启用它,但如果您有大量数据,这是一个非常耗费资源的操作,因为在FK时都需要重新检查重新启用。

基本上,您要么必须使用不完整的声明性引用完整性,要么更明智地考虑重新构建您的数据,如@munisor建议的那样。

警告:以下代码说明了如何创建圆形FK,但这对您的健康非常不利!我相信从长远来看,你不会想要这样做。例如,在运行这些表之后简单地尝试删除其中任何一个表非常困难,你不能简单DROP TABLE

CREATE TABLE [A]
(
    [AId] int
        NOT NULL
        PRIMARY KEY,
    [BId] int
        NULL
        -- You can't create the reference to B here since it doesn't yet exist!
)

CREATE TABLE [B]
(
    [BId] int
        NOT NULL
        PRIMARY KEY,
    [AId] int
        NOT NULL
        FOREIGN KEY
            REFERENCES [A]
)

-- Now that B is created, add the FK to A
ALTER TABLE [A]
    ADD
        FOREIGN KEY ( [BId] )
        REFERENCES [B]

ALTER TABLE [A]
    ALTER COLUMN [BId]
        int
        NOT NULL

答案 1 :(得分:1)

在多租户方案中,假设您有2个表:订阅者和订阅者接触。

您希望一眼就知道谁是订阅者的主要联系人。但是,Contact表还必须具有对SubscriberId的FKEY引用,以便进行分区/用于联合密钥等(SQL Azure)。

=======================================================================
Subscriber.sql 
=======================================================================
-- One who has subscribed to Rhipheus
CREATE TABLE [rhipheus].[Subscriber]
(
    [Id] UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() CONSTRAINT [PKEY_Subscriber_Id] PRIMARY KEY CLUSTERED,
    [ShortName] NVARCHAR(50) NOT NULL,
    [LegalName] NVARCHAR(255) NOT NULL,
    [SmallLogoPath] NVARCHAR(MAX) NOT NULL,
    [LargeLogoPath] NVARCHAR(MAX) NOT NULL,
    [PrimaryContactId] UNIQUEIDENTIFIER NULL REFERENCES [rhipheus].[Contact]([Id]),
)

====================================================================
Contact.sql
====================================================================
CREATE TABLE [rhipheus].[Contact]
(
    [Id] UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() CONSTRAINT [PKEY_Contact_Id] PRIMARY KEY CLUSTERED,
    [SubscriberId] UNIQUEIDENTIFIER NOT NULL CONSTRAINT [FKEY_Contact_SubscriberId_Subscriber_Id] REFERENCES [rhipheus].[Subscriber]([Id]),
    [FirstName] NVARCHAR(50) NOT NULL,
    [LastName] NVARCHAR(50) NOT NULL,
)

这曾经用于工作2010数据库项目,因为它曾用于去除所有列级约束并使用单独的ALTER脚本创建它们。

我在VS.Net 2012中解决此问题的方法是将外键列声明为NULLable,并使用单独的ALTER语句在Subscriber上添加外键。当然,VS 2012中的SQL Server项目不允许我在列级别进行声明,因为它无法确定首先创建哪个表(即使HINT在那里以NULLable的形式存在)声明)。

=======================================================================
Subscriber.sql 
=======================================================================
-- One who has subscribed to Rhipheus
CREATE TABLE [rhipheus].[Subscriber]
(
    [Id] UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() CONSTRAINT [PKEY_Subscriber_Id] PRIMARY KEY CLUSTERED,
    [ShortName] NVARCHAR(50) NOT NULL,
    [LegalName] NVARCHAR(255) NOT NULL,
    [SmallLogoPath] NVARCHAR(MAX) NOT NULL,
    [LargeLogoPath] NVARCHAR(MAX) NOT NULL,
    [PrimaryContactId] UNIQUEIDENTIFIER NULL 
)

====================================================================
Contact.sql
====================================================================
CREATE TABLE [rhipheus].[Contact]
(
    [Id] UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() CONSTRAINT [PKEY_Contact_Id] PRIMARY KEY CLUSTERED,
    [SubscriberId] UNIQUEIDENTIFIER NOT NULL CONSTRAINT [FKEY_Contact_SubscriberId_Subscriber_Id] REFERENCES [rhipheus].[Subscriber]([Id]),
    [FirstName] NVARCHAR(50) NOT NULL,
    [LastName] NVARCHAR(50) NOT NULL
)

====================================================================
Subscriber.ForeignKeys.sql
====================================================================
ALTER TABLE [rhipheus].[Subscriber] ADD CONSTRAINT [FKEY_Subscriber_PrimaryContactId_Contact_Id] FOREIGN KEY([PrimaryContactId]) REFERENCES [rhipheus].[Contact]([Id])
GO