问题描述
||
数据库关系周期闻起来像不良的数据库设计。以下是我认为无法避免的情况:
公司有地点(城市)
公司拥有产品(巨无霸)
产品无法在以下地区使用(沙特阿拉伯没有培根汉堡)
当前的设计将允许您在确实属于该公司的位置上提供不属于该公司的产品。
公司
1-麦当劳
2-汉堡王
位置
1-纽约,建筑物1-麦当劳(1)
2-阿姆斯特丹,2号楼-汉堡王(2)
产品
1-巨无霸-麦当劳(1)
产品位置
1-巨无霸(1)-阿姆斯特丹,建筑2(2)
麦当劳卖巨无霸,汉堡王不卖,但看来他们的大楼有卖:)
当我们向也依赖于位置的产品添加关系时,情况变得更糟。
我该怎么做才能防止周期?
如何确保数据库数据完整性?
解决方法
如果我们以
Location
,Company
和Product
作为独立实体开始-我认为您尝试过:
create table ProductAtLocation (
CompanyID integer,LocationID integer,ProductID integer
);
alter table ProductAtLocation
add constraint pk_ProdLoc primary key (CompanyID,LocationID,ProductID),add constraint fk1_ProdLoc foreign key (CompanyID,LocationID) references CompanyLocation (CompanyID,LocationID),add constraint fk2_ProdLoc foreign key (CompanyID,ProductID) references CompanyProduct (CompanyID,ProductID)
;
并且如果Product
是从属实体(取决于公司):
,循环依赖关系不会自动“坏的数据库设计”。从概念建模的角度来看,如果这样的依赖关系准确地代表了您要建模的对象,那么就不是“错误”。
不幸的是,SQL的局限性常常使得很难或不可能强制执行周期性的约束。在SQL中,通常必须通过某种方式打破约束或通过在过程代码中而不是通过数据库约束来实现规则来妥协。
,您真正需要的SQL“断言”。但是,不幸的是,当前没有DBMS支持这些功能。该断言将类似于:
assertion product_location_check
check (not exists (select null
from company_product_location cpl
where not exists
( select null
from company_products cp
join company_locations cl on c1.company_id = cp.company_id
and cp.product_id = cpl.product_id
and cl.location_id = cpl.location_id
and cp.company_id = cpl.company_id
)
)
);
如果没有这些,将设置密钥的另一种可能性,以便可以检查规则:
create table company_products
( company_id references companies,product_id ...,primary key (company_id,product_id)
);
create table company_locations
( company_id references companies,location_id ...,location_id)
);
create table company_product_locations
( company_id ...,product_id,location_id),foreign key (company_id,product_id) references company_products),location_id) references company_locations)
);
这样可以确保每个company_product_locations都引用与同一公司关联的产品和位置。
复杂约束的另一种可能性是使用实体化视图。我在这里在Oracle上下文中写过有关此的博客。
,我不同意-这句话是不正确的:
当前的设计将使您能够
提供不属于的产品
这家公司
如果产品不属于某个公司,则该产品将没有该公司的外键。一个公司可能有许多产品,但是一个产品只能属于一个公司。那是一对多的关系。
对于产品位置,这听起来像是多对多的关系:可以在许多位置提供产品,而一个位置可以出售许多产品。您需要一个Product_Location JOIN表。
更新:
您添加的记录只能解决此问题。一个位置不仅仅是一栋建筑物。麦当劳和汉堡王可能在同一栋楼里,但是他们不在同一栋楼里。您的位置表除街道地址外还需要其他列。我的评论仍然有效。如果设计正确,汉堡王将无法出售巨无霸。您没有正确的选择;因此你的困惑。
,问题的一部分是麦当劳和汉堡王都出售被称为“汉堡”和“芝士汉堡”以及(我认为)“双芝士汉堡”的产品。因此,您存储在ProductLocation中的信息不完整。
Product
--
Big Mac McDonald\'s
Hamburger McDonald\'s
Hamburger Burger King
ProductLocation
Big Mac McDonald\'s New York,building 1
Hamburger McDonald\'s New York,building 1
Hamburger Burger King Amsterdam,building 2
达菲(Duffymo)说“位置远不止是建筑物”是对的。
这是实现这些约束的一种方法。我删除了ID号,因为它们倾向于隐藏实际情况。
create table company (
co_name varchar(15) primary key
);
insert into company values
(\'McDonald\'\'s\'),(\'Burger King\');
create table location (
loc_name varchar(30) primary key,co_name varchar(15) not null references company (co_name),unique (loc_name,co_name)
);
insert into location values
(\'New York,building 1\',\'McDonald\'\'s\'),(\'Amsterdam,building 2\',\'Burger King\');
create table product (
co_name varchar(15) not null references company (co_name),product_name varchar(15) not null,primary key (co_name,product_name)
);
insert into product values
(\'McDonald\'\'s\',\'Big Mac\'),(\'McDonald\'\'s\',\'Hamburger\'),\'Cheeseburger\'),(\'Burger King\',\'Cheeseburger\');
create table product_location (
loc_name varchar(30) not null references location (loc_name),co_name varchar(15) not null,foreign key (co_name,product_name) references product (co_name,product_name),foreign key (loc_name,co_name) references location (loc_name,co_name),primary key (loc_name,co_name,product_name)
);
insert into product_location values
(\'Amsterdam,\'Burger King\',\'Cheeseburger\');
注意product_location中重叠的外键。重叠的外键可确保使用位置标识的公司和使用产品标识的公司是同一公司。现在,以下INSERT将失败,并违反外键约束。
insert into product_location values
(\'Amsterdam,\'McDonald\'\'s\',\'Cheeseburger\');