SQL约束以防止不匹配的外键

时间:2016-08-07 03:35:42

标签: sql sql-server foreign-keys constraints

我有三个表,CustomerPhoneNumberPhoneCall

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

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

我想创建一个约束,阻止条目进入PhoneCall表,其中引用的PhoneNumber不属于引用的Customer

以下是我的表格:

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
}

这似乎应该很容易通过约束来解决,但我真的很难弄清楚应该怎么做。是否有一种约束来解决这个问题?

更新

感谢Kentaro的回答。

我最喜欢这个意思是,如果没有指定电话号码,我可以将PhoneNumberId的电话记录设置为空。

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

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]);

2 个答案:

答案 0 :(得分:3)

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

只有一个客户ID,就不会发生冲突。

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

答案 1 :(得分:1)

外键约束可以轻松地管理它(您已经将其作为标记)。您所要做的就是稍微更改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);