数据库关系周期就像糟糕的数据库设计一样。以下是我认为无法阻止的情况:
公司有产品(巨无霸)
产品不适用于地点(沙特阿拉伯没有培根汉堡)
目前的设计允许您在位置上提供不属于公司的产品 确实属于此公司。
公司
1 - 麦当劳
2 - 汉堡王
位置
1 - 纽约,1号楼 - 麦当劳(1)
2 - 阿姆斯特丹,2号楼 - 汉堡王(2)
产品
1 - 巨无霸 - 麦当劳(1)
产品展示您
1 - 巨无霸(1) - 阿姆斯特丹,2号楼(2)
麦当劳卖掉巨无霸,汉堡王没卖,但看起来他们的建筑确实:) 当我们向产品添加与位置相关的关系时,情况会变得更糟。
如何防止循环? 如何确保数据库数据完整性?
答案 0 :(得分:5)
循环依赖不会自动“糟糕的数据库设计”。从概念建模的角度来看,如果这种依赖关系准确地表示了您要模拟的内容,那么它就不是“错误的”。
不幸的是,SQL的局限性常常使得强制或不可能强制执行周期性约束。在SQL中,您通常必须通过以某种方式破坏约束或通过在过程代码中实现规则而不是通过数据库约束来妥协。
答案 1 :(得分:3)
如果我们从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
是一个依赖实体(取决于公司):
答案 2 :(得分:2)
你真正需要的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 ...
, primary key (company_id, location_id)
);
create table company_product_locations
( company_id ...
, product_id ...
, location_id ...
, primary key (company_id, product_id, location_id)
, foreign key (company_id, product_id) references company_products)
, foreign key (company_id, location_id) references company_locations)
);
这可确保每个company_product_locations引用与同一公司关联的产品和位置。
复杂约束的另一种可能性是使用物化视图。我在Oracle here的背景下写了这篇博文。
答案 3 :(得分:0)
我不同意 - 这句话不正确:
目前的设计可以让你 提供不属于的产品 这家公司
如果产品不属于公司,则该公司不会拥有外键。公司可能有很多产品,但产品只能属于一家公司。这是一对多的关系。
对于产品位置,这听起来像是多对多关系:产品可以在许多地方提供,而位置可以销售许多产品。您需要一个Product_Location JOIN表。
更新:
您添加的记录仅说明问题。位置不仅仅是建筑物;麦当劳和汉堡王可能在同一栋楼里,但他们不在同一栋楼里。除街道地址外,您的位置表还需要其他列。我的意见仍然有效。如果您正确设计,汉堡王将无法销售巨无霸。你没有权利;因此你的困惑。
答案 4 :(得分:0)
问题的部分原因是麦当劳和汉堡王都出售名为“汉堡包”和“芝士汉堡”的产品和(我认为)“双层芝士汉堡”。因此,您在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也是对的。
这是实现这些约束的一种方法。我丢弃了身份证号码,因为他们倾向于隐藏真正发生的事情。
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'),
('McDonald''s', 'Cheeseburger'),
('Burger King', 'Hamburger'),
('Burger King', 'Cheeseburger');
create table product_location (
loc_name varchar(30) not null references location (loc_name),
co_name varchar(15) not null,
product_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, building 2', 'Burger King', 'Cheeseburger');
请注意product_location中重叠的外键。重叠的外键保证公司所识别的位置和与产品一致的公司是同一家公司。现在,以下INSERT将因外键约束违规而失败。
insert into product_location values
('Amsterdam, building 2', 'McDonald''s', 'Cheeseburger');