5

我正在使用 SQL Server 2012

我有两个表,Person并且CouplePerson有一个外键来自“Person” CoupleCouple外键来自“Person”。

当我尝试向每个表中插入数据时,会发生错误,因为每个表都有来自另一个表的 FK,并且最初都是空的。

CREATE TABLE [dbo].[Couple](
    [CoupleId] [int] IDENTITY(1,1) NOT NULL,
    [HusbandPersonId] [int] NOT NULL,
    [WifePersonId] [int] NOT NULL,
    [StartDate] [date] NOT NULL,
    [EndDate] [date] NOT NULL,
 CONSTRAINT [PK_Couple] PRIMARY KEY CLUSTERED 
(
    [CoupleId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

CREATE TABLE [dbo].[Person](
    [PersonId] [int] IDENTITY(1,1) NOT NULL,
    [ChildOfCoupleId] [int] NOT NULL,
    [CityOfBirth] [int] NOT NULL,
    [CityOfPermanentResidence] [int] NOT NULL,
    [CityOfCurrentResidence] [int] NOT NULL,
    [FirstName] [varchar](20) NOT NULL,
    [LastName] [varchar](20) NOT NULL,
    [BirthDate] [date] NOT NULL,
    [DeathDate] [date] NOT NULL,
    [IsMale] [bit] NOT NULL,
 CONSTRAINT [PK_Person] PRIMARY KEY CLUSTERED 
(
    [PersonId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY

ALTER TABLE [dbo].[Couple]  WITH CHECK ADD  CONSTRAINT [FK_Couple_Person] FOREIGN KEY([HusbandPersonId])
REFERENCES [dbo].[Person] ([PersonId])
GO
ALTER TABLE [dbo].[Couple] CHECK CONSTRAINT [FK_Couple_Person]
GO
ALTER TABLE [dbo].[Couple]  WITH CHECK ADD  CONSTRAINT [FK_Couple_Person1] FOREIGN KEY([WifePersonId])
REFERENCES [dbo].[Person] ([PersonId])
GO
ALTER TABLE [dbo].[Couple] CHECK CONSTRAINT [FK_Couple_Person1]
GO
ALTER TABLE [dbo].[Person]  WITH CHECK ADD  CONSTRAINT [FK_Person_Couple] FOREIGN KEY([ChildOfCoupleId])
REFERENCES [dbo].[Couple] ([CoupleId])
GO
ALTER TABLE [dbo].[Person] CHECK CONSTRAINT [FK_Person_Couple]
GO

如何解决这个问题?

4

2 回答 2

3

你不能要求所有人都有父母,因为你会遇到父母递归的无限循环。也就是说,你必须有一对没有父母的“亚当和夏娃”夫妇才能成为所有孩子的父母。

现在,您可以将ChildOfCoupleId列设为 NULLable 作为一种解决方案。但坦率地说,我认为使用CoupleId并不是最佳的。相反,使用MotherIdFatherId。这最终会更好,因为它:

  1. 不需要你知道这两个元素——如果你只知道一个父母,它可以被输入。

  2. 通过使 FKPerson指向Person而不是必须查询以确定人员的另一个表来简化 FK 关系。

  3. 不会强迫您为父母Couple的参与定义一个开始和停止日期——我注意到您没有调用它,Marriage但您正在使用Husbandand Wife。因此,如果您想对真实的、破碎的世界进行建模,则不一定要知道这些信息。

  4. 将孩子的父母与这对夫妇在一起的确切时间分开。如果一对夫妇结婚、离婚和再婚,那么您将指定孩子是在哪次婚姻中怀孕的。对于简单的血统,这是不必要的——你只需要了解父母。如果婚姻无效,但他们设法怀孕了孩子怎么办?那么你将如何存储亲子信息呢?在我们的世界中,父母身份与夫妻身份是截然不同的事实。

最后,通常最好避免 NULLable 列。您可以通过创建一个新表来完成此操作,例如Parentage

CREATE TABLE dbo.Parentage (
   PersonID int NOT NULL
      CONSTRAINT FK_Parentage_Person
      FOREIGN KEY REFERENCES dbo.Person (PersonID),
   ParentID int NOT NULL,
   ParentIsMale bit NOT NULL,
   CONSTRAINT PK_Parentage PRIMARY KEY CLUSTERED (PersonID, ParentIsMale),
   CONSTRAINT FK_Parentage_Parent FOREIGN KEY (ParentID, ParentIsMale)
      REFERENCES dbo.Person (ParentID, IsMale)
);

您必须(ParentId, IsMale)Person表中添加唯一约束或唯一索引才能为其创建 FK。作为 PK/FK 的一部分使用IsMale可确保您得到一位母亲和一位父亲。

虽然这种设计可能看起来很笨重,但从长远来看,它会带来真正的好处。一个人的父亲或母亲的知识是通过插入一行而不是更新一个可为空的列来编码的。如果没有行,则父项未知。

这种设计也很容易适应记录不同类型的父母:遗传、出生、收养。一个人可能有两个遗传父母和一个生母。然后通过一系列灾难性事件,所有父母都被杀并被收养(甚至不止一次)。因此,您需要更改 PK,然后将ParentTypeIDFromDateToDate列添加到表中。需要进一步调整以限制不恰当的关系被表示(例如“生父”)。

于 2012-12-27T20:46:46.850 回答
0

我认为您必须认真考虑更改此架构。除了插入数据的问题外,当您尝试删除数据时,您也会遇到完全相同的问题。marc_c 给出的解决方案,使用 null 有助于一些,但不是最好的事情。您能做的最好的事情就是将另一张桌子带入您的设计中,并在此处提供对夫妇的参考。

于 2012-12-24T09:04:13.207 回答