我有一个包含许多表的数据库,除了一点点之外一切看起来都不错......
Inventory Table <*-----1> Storage Table <1-----1> Van Table
^
1
|-------1> Warehouse Table
使用 Storage 表是因为 Van 和 Warehouse 表很相似,但是如何在 Storage 和 Warehouse/Van 表之间创建关系?它们必须是 1 到 1 是有道理的,因为存储对象只能是 1 个存储位置和类型。 我确实将 Van/Warehouse 表链接到 StorageId 主键,然后添加约束以确保 Van 和 Warehouse 表不具有相同的 StorageId,但这似乎可以通过更好的方式完成。
我可以看到几种方法,但它们似乎都是错误的,所以任何帮助都会很好!
最佳答案
您正在使用继承(在实体关系建模中也称为“子类”或“类别”)。一般来说,在数据库中有3种表示方式:
- “一个表中的所有类”:只有一个表“覆盖”父类和所有子类(即具有所有父列和子列),并带有 CHECK 约束以确保正确的子集的字段是非 NULL(即两个不同的 child 不“混合”)。
- “每个表的具体类”:每个 child 都有一个不同的表,但没有父表。这需要在所有 child 中重复 parent 的关系(在您的情况下为库存 <- 存储)。
- “每张 table 上课”:为每个 child 准备一个父表和一个单独的表,这就是您想要做的。这是最干净的,但可能会降低一些性能(主要是在修改数据时,查询时不是那么多,因为您可以直接从子节点加入并跳过父节点)。
我通常更喜欢第 3 种方法,但在应用程序级别强制 child 的存在 和排他性。在数据库级别强制执行这两者有点麻烦,但如果 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
) 的 child 。
不幸的是,MS SQL Server does not support deferred constraints ,但您可以将整个操作“隐藏”在存储过程后面,并禁止客户端直接修改表。
可以在没有延迟约束的情况下强制执行排他性:
STORAGE_TYPE
是一个类型鉴别符,通常是一个整数以节省空间(在上面的示例中,0 和 1 对您的应用程序是“已知的”并相应地进行解释)。
VAN.STORAGE_TYPE
和 WAREHOUSE.STORAGE_TYPE
可以计算(也称为“计算”)列以节省存储空间并避免需要 CHECK
s.
--- 编辑 ---
计算列将在 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的虚拟列),可以节省一些存储空间。
关于sql - 如何创建多个一对一,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9174200/