【问题标题】:SQL constraint to prevent mismatched foreign keys防止外键不匹配的 SQL 约束
【发布时间】:2016-08-07 03:35:42
【问题描述】:

我有三个表,CustomerPhoneNumberPhoneCall

PhoneNumber 包含电话号码,每个电话号码都属于一个客户。

PhoneCall 包含通话记录,并引用了被呼叫的客户以及被呼叫的电话号码。

我想创建一个约束,防止在引用的PhoneNumber 不属于引用的CustomerPhoneCall 表中输入条目。

这是我的桌子:

Customer
    Id
    Name

PhoneNumber
    Id
    CustomerId (fk to Customer.Id)
    AreaCode
    Number

PhoneCall
    CustomerId (fk to Customer.Id)
    PhoneNumberId (fk to PhoneNumber.Id)
    Description
    Duration

客户和电话号码示例:

Customers = [
    { Id: 1, Name: 'Bob' },
    { Id: 2, Name: 'Richard' },
];

PhoneNumbers = [
    { Id: 1, CustomerId: 1, AreaCode: 'xxx', Number: 'xxxx' },
    { Id: 2, CustomerId: 2, AreaCode: 'yyy', Number: 'yyyy' }
];

我想阻止这样的条目:

PhoneCall = {
    CustomerId: 1,
    PhoneNumber: 2,
    Description: 'call to customer',
    Duration: 5
}

这似乎应该很容易通过约束来解决,但我真的很难弄清楚应该如何完成。有没有一种约束可以解决这个问题?

更新

感谢健太郎的回答。

我最喜欢它的地方在于,如果没有指定电话号码,我可以将 PhoneNumberId 设置为 null 来记录电话。

这是我用来添加约束的查询

ALTER TABLE [PhoneNumbers] ADD UNIQUE ([Id], [CustomerId]);
ALTER TABLE [dbo].[PhoneCalls] ADD  CONSTRAINT [FK_dbo.PhoneCalls_dbo.ReferencedPhoneNumberMustBelongToReferencedCustomer] FOREIGN KEY([PhoneNumberId], [CustomerId]) REFERENCES [dbo].[PhoneNumbers] ([Id], [CustomerId]);

【问题讨论】:

  • 我删除了不兼容的数据库标签。随意为您真正使用的数据库添加标签。

标签: sql sql-server foreign-keys constraints


【解决方案1】:

我认为解决您的问题的最简单方法是:不要在 PhoneCall 表中包含 CustomerId。通过PhoneNumbers 表查找。

只有一个客户 ID,不可能发生冲突。

PhoneNumbers(CustomerId, PhoneNumber) 上构建冗余唯一索引的另一种可能性。然后将此索引用于外键引用。它将要求客户 ID 匹配。

【讨论】:

  • 您说得对,在 PhoneCall 表中不包括 CustomerId 将是上述场景的最简单解决方案,但我已经大大简化了场景,以使问题更易于理解和回答。在我的真实世界场景中,确实需要 CustomerId 和 PhoneNumberId 都存在于 PhoneCall 表中。
  • @NoPyGod 。 . .然后使用第二种解决方案。
【解决方案2】:

外键约束可以轻松管理这个(你已经拥有一个标签)。您所要做的就是稍微更改PhoneCall 表中的表定义和外键引用,以便它将引用PhoneNumber 表中的CustomerID。但为了这样做,您必须稍微更改 PhoneNumber 和 PhoneCall 的表定义,因为外键需要是引用表中的主键或候选键(即唯一)。因此,使用提供的表定义,在 TSQL 中重新创建这些表将如下所示(使用假定的数据类型)。

CREATE TABLE PhoneNumber(
    Id INT UNIQUE,
    CustomerId INT UNIQUE REFERENCES Customer(ID),
    AreaCode INT(3),
    Number INT(7),
    CONSTRAINT CK_PhoneID_CustID (ID, CustomerID));

CREATE TABLE PhoneCall (
    CustomerId INT REFERENCES Phonenumber(CustomerID),
    PhoneNumberId  INT REFERENCES PhoneNumber(Id),
    Description NVARCHAR(MAX),
    Duration INT);

【讨论】:

    猜你喜欢
    • 2012-04-03
    • 1970-01-01
    • 2013-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-10
    • 2017-06-17
    • 1970-01-01
    相关资源
    最近更新 更多