我的大部分sql代码都是生成的(来自POD)。现在我有问题,表user_data有一个FK到ref_status,它指向两个不同的user_data。我的代码执行以下操作
然后我得到了例外
Foreign key 'FK__...__0F975522' references invalid table 'user_data'.
如果他们同时使用彼此作为参考,我如何创建两个表?我认为,因为它是在同一个交易中,它会起作用。我还要注意这个代码在sqlite中工作正常,启用了FK支持(自上个月发布的System.Data.SQLite以来一直支持)。那么我期望如何创建这两个表呢?
答案 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