我有一个包含许多表格的数据库,除了一点之外它看起来都很好......
Inventory Table <*-----1> Storage Table <1-----1> Van Table
^
1
|-------1> Warehouse Table
由于Van和Warehouse表相似,因此使用了Storage表,但如何在Storage和Warehouse / Van表之间创建关系?因为存储对象只能是1个存储位置和类型,所以它们需要为1比1。 我确实有Van / Warehouse表链接到StorageId主键,然后添加一个约束来确保Van和Warehouse表没有相同的StorageId,但这似乎可以做得更好。
我可以看到这样做的几种方法,但它们看起来都错了,所以任何帮助都会很好!
答案 0 :(得分:17)
您正在使用继承(在实体关系建模中也称为“子类”或“类别”)。通常,有3种方法可以在数据库中表示它:
我通常更喜欢第三种方法,但在应用程序级别强制执行子项的存在和排他性。在数据库级别执行这两者有点麻烦,但如果DBMS支持延迟约束,则可以完成。例如:
CHECK (
(
(VAN_ID IS NOT NULL AND VAN_ID = STORAGE_ID)
AND WAREHOUSE_ID IS NULL
)
OR (
VAN_ID IS NULL
AND (WAREHOUSE_ID IS NOT NULL AND WAREHOUSE_ID = STORAGE_ID)
)
)
这将强制执行排他性(由于CHECK
)和孩子的存在(由于CHECK
和FK1
/ FK2
的组合)。
不幸的是,MS SQL Server does not support deferred constraints,但您可以“隐藏”存储过程背后的整个操作,并禁止客户端直接修改表。
只有排他性可以在没有延期约束的情况下执行:
STORAGE_TYPE
是一个类型鉴别器,通常是一个节省空间的整数(在上面的例子中,0和1对应用程序是“已知的”并相应地进行解释)。
VAN.STORAGE_TYPE
和WAREHOUSE.STORAGE_TYPE
可以计算(也就是“计算”)列,以节省存储空间并避免需要CHECK
。
---编辑---
计算列可以在SQL Server下运行,如下所示:
CREATE TABLE STORAGE (
STORAGE_ID int PRIMARY KEY,
STORAGE_TYPE tinyint NOT NULL,
UNIQUE (STORAGE_ID, STORAGE_TYPE)
);
CREATE TABLE VAN (
STORAGE_ID int PRIMARY KEY,
STORAGE_TYPE AS CAST(0 as tinyint) PERSISTED,
FOREIGN KEY (STORAGE_ID, STORAGE_TYPE) REFERENCES STORAGE(STORAGE_ID, STORAGE_TYPE)
);
CREATE TABLE WAREHOUSE (
STORAGE_ID int PRIMARY KEY,
STORAGE_TYPE AS CAST(1 as tinyint) PERSISTED,
FOREIGN KEY (STORAGE_ID, STORAGE_TYPE) REFERENCES STORAGE(STORAGE_ID, STORAGE_TYPE)
);
-- We can make a new van.
INSERT INTO STORAGE VALUES (100, 0);
INSERT INTO VAN VALUES (100);
-- But we cannot make it a warehouse too.
INSERT INTO WAREHOUSE VALUES (100);
-- Msg 547, Level 16, State 0, Line 24
-- The INSERT statement conflicted with the FOREIGN KEY constraint "FK__WAREHOUSE__695C9DA1". The conflict occurred in database "master", table "dbo.STORAGE".
不幸的是,SQL Server要求在外部键中使用的计算列为PERSISTED。其他数据库可能没有此限制(例如Oracle的虚拟列),这可以节省一些存储空间。
答案 1 :(得分:1)
正如你所说,有很多解决方案。我建议从最简单的解决方案开始,然后在性能或存储成为问题时进行优化。最简单的解决方案(但在存储方面不是最佳的)是使存储表具有存储类型的列(指示行是代表货车还是仓库),还有Van属性列和Warehouse属性。在表示Van的行中,Warehouse属性的列都将为null。在表示Warehouse的行中,Van属性的列都将为null。
这样,您减少了表的数量,并使您的查询保持简洁。如果存储紧张,请准备好重新审视您的决定。
答案 2 :(得分:1)
在我看来,库存商品可能会改变位置,所以我会选择这样的东西。