运行SQL-server 2005并使用简化版本的数据库模型,如下图所示。
该模型工作得很好但我的问题是当我想在Subscriptions和Addresses或Ports之间添加引用时。如果我在Subscriptions.port_id和Ports.id之间创建一个普通的FK,那么可能会喜欢一个引用另一个客户的端口/地址,而不是订阅引用的客户。
我想知道是否可以避免此问题,或者我是否必须处理代码中的问题并且存在数据不一致的风险?
备注 地址和端口是预先填充的,并且在创建客户之前存在
订阅 一些订阅将连接到一个地址,一些订阅将连接到端口,一些订阅将只与客户建立连接。
答案 0 :(得分:3)
我想知道是否可以避免此问题,或者我是否必须处理代码中的问题并且存在数据不一致的风险?
您的模特违反了3NF。
请勿在订阅中存储address_id
和customer_id
,因为它们由port_id
唯一定义。
相反,只存储port_id
并通过联接获取address_id
和customer_id
。
答案 1 :(得分:1)
1)Quassnoi说什么
2)如果出于某种原因,您确实希望维护表格的当前结构:
ALTER TABLE Addresses ADD CONSTRAINT UQ_Addresses_WithCustomers (Id,Customer_ID)
ALTER TABLE Ports ADD CONSTRAINT UQ_Ports_WithAddresses (Id,Address_ID)
这些将超级密钥添加到这两个表中,以便外键约束可以引用它们
ALTER TABLE Subscriptions ADD
CONSTRAINT FK_Subscription_Addresses (Address_ID,Customer_ID)
references Addresses (Id,Customer_ID)
ALTER TABLE Subscriptions ADD
CONSTRAINT FK_Subscription_Ports (Port_ID,Address_ID)
references Ports (ID, Address_ID)
这些外键现在强制您匹配address表中与address_id和client_id匹配的行。
而且,大概你还没有这个:
ALTER TABLE Subscriptions ADD
CONSTRAINT CK_Subscription_EnhancedNUllability CHECK
(Port_ID is null or Address_ID is not null)
因为否则你可能在subscriptions表中有一个null address_id和一个非null的port_id,并且这是不可检查的(外键约束不适用,因为address_id为null)
Re:备注地址和端口已预先填充并在创建客户之前存在
我为这些创建单独的表,没有可空列,然后引入具有这些Address,Ports和Client表的外键的新表(ClientAddresses,ClientPorts)。然后更改上面的内容,让Subscriptions引用ClientAddresses / Ports表。简而言之,我用可空列留下的唯一表就是订阅表,上面的约束就足够了。所以你有:
CREATE TABLE Addresses (
AddressID int IDENTITY(1,1) not null,
Name nchar(10) not null,
constraint PK_Addresses PRIMARY KEY (AddressID),
constraint UQ_Address_Names UNIQUE (Name)
)
CREATE TABLE ClientAddresses (
ClientID int not null,
AddressID int not null,
constraint PK_ClientAddresses PRIMARY KEY (AddressID) /*This also prevents the same address being assigned to multiple clients */,
constraint FK_ClientAddresses_Clients FOREIGN KEY (ClientID) references Clients (ID),
constraint FK_ClientAddresses_Addresses FOREIGN KEY (AddressID) references Addresses (AddressID),
constraint UQ_ClientAddresses_WithClients UNIQUE (ClientID,AddressID)
)
将UQ_ClientAddresses_WithClients
用作订阅的外键关系的目标。
答案 2 :(得分:1)