具有两个相似功能的字段的数据库设计中的需求冲突
好的,现在我要为"盒子物品"制作一张桌子。
现在,根据使用的物品/物品的状态,盒子物品最终可能与"运输"盒子或者"退货"盒子有关。
装箱物料可能有缺陷:如果是,则将在装箱物料的行中设置一个标记(IsDefective),并将该装箱物料放置在"退货"框中(其他物料将退还给该供应商)。否则,盒装物品最终将被放入"运输"盒中(与其他物品一起运输)。 (请注意,"装运箱"和"退货箱"都有自己的表:所有箱子都没有一个公用表...尽管也许我应该考虑这样做,作为第三种可能性?)
也许今天我只是想的不够清楚,但是我开始质疑在这种情况下应该怎么做。
我的直觉告诉我,即使在给定的时间只能发生一种关系,每个可能的关系也应该有一个单独的字段,这会使Box Items的模式看起来像:
BoxItemID
描述
有缺陷
ShippingBoxID
ReturnBoxID
等等...
这样可以使关系明确,但看起来很浪费(因为任何时候都只使用其中一种关系)。因此,我以为BoxID只能有一个字段,然后根据IsDefective字段确定它所指的是哪个BoxID(运输或者退货箱ID):
BoxItemID
描述
有缺陷
BoxID
等等...
这似乎不那么浪费,但并不适合我。这种关系并不明显。
因此,我把它交给了Stackoverflow的数据库专家。在这个情况下,你会怎么做?
编辑:谢谢大家的投入!这给了我很多思考。例如,下一次启动这样的项目时,我将使用ORM。 =)有两个,因为我现在不在,所以我要咬四个字节并使用两个字段。
再次感谢大家!
解决方案
我们在说的是多态关系。可以引用多个其他表的单个ID。有几种支持此功能的框架,但是,这对数据库完整性(可能)是不利的(数据库或者应用程序是否应保持参照完整性可能是其他讨论。)
那这个呢?
BoxItem: BoxItemID, Description, IsDefective Box: BoxID, Description BoxItemMap: BoxID, BoxItemID, BoxItemType
然后,我们可以让BoxItemType为枚举,或者为整数,在其中将应用程序中的常量定义为"返回"或者"发货"作为盒子的类型。
我会考虑为包装盒制作一张桌子,并且包装盒类型是包装盒表的一列。这将简化关系,并使仍然容易查询框类型。因此,box项只有一个boxId外键。
同意上面讨论的多态性,尽管它可能被不良使用,但仍然是可行的解决方案。
基本上,我们有一个称为box的基表。然后,我们还有另外两个表,装运箱和退货箱。这两个字段会添加任何特殊的字段。它们与1:1 fk的盒子有关.Boz基本表具有所有盒子类型的公共字段。
我们将BoxItem与Box表相关联。获取正确的框类型的方法是通过执行基于键将子框与根框连接起来的查询。基本框和子框中都具有的记录就是该类型。
我们只需要小心,就像前面提到的那样,当我们创建一个框类型时,它就可以正确完成。没错,这就是测试的目的。添加它们的代码只需编写一次。或者使用ORM。
几乎所有ORM都支持该策略。
我与Psychotic Venom和Mattlant在一起。
采取多态路线(必须根据另一个字段的内容来确定外键指向的表)会很痛苦。编写约束可能会很困难(我不确定大多数数据库是否会本地支持,我认为我们必须使用触发器)。
桌子之间有物品移动过吗?坚持使用两个具有相同定义的表,其中一个表用于退货,一个表用于运输可能是最简单的方法。如果我们要坚持我们首先提出的定义(使用两个单独的字段),那是完全合理的。
"过早的优化是万恶之源"。虽然看起来很浪费,但请记住要存储的内容。由于它们是ID,因此它们可能只是整数,可能是4个字节。每条记录浪费四个字节基本上没有什么。实际上,由于使用填充将内容放在均匀的地址或者其他类似内容上,因此可以在其中多余的字段"自由"放置。这完全取决于数据库的设计。
除非我们有充分的理由采用多态路线(例如,我们位于内存不足的嵌入式系统上,或者必须在某些非常慢的9600bps链路上进行复制),否则我们可能不值得为之头痛。必须将所有这些特殊情况写入查询中会变得很烦人。
快速示例:在两个表之间进行联接,如果要联接,则基于isDefective标志是否已设置将是一件很麻烦的事情。至少对于我来说,仅使用两个专栏之一就足以节省麻烦。
我可能会选择:
BoxTable: box_id, box_descrip, box_status_id ... 1, Lovely Box, 1 2, Borked box, 2 3, Ugly Box, 3 4, Flammable Box, 4 BoxStatus: box_status_id, box_status_name, box_type_id, .... 1,Shippable, 1 2,Return, 2 3,Ugly, 2 4,Dangerous,3 BoxType: box_type_id, box_type_name, ... 1, Shipping box, ... 2, Return box, .... 3, Hazmat box, ...
这样,"包装箱状态"定义了包装箱类型,如果以后需要扩展到更多的状态级别或者包装箱类型,它会很灵活。
我只需要一个BoxItems表,其中包含IsDefective,ShippingBoxID,与装运箱相关的字段,ReturnBoxID和与退货箱相关的字段。对于每个记录,某些字段将始终为NULL。
这是一个非常简单且不言而喻的设计,下一个开发人员不大可能被它弄糊涂。从理论上讲,这种设计效率低下,因为要保证每一行的空字段。在实践中,无论如何,数据库倾向于为每一行具有最小的所需存储大小,因此(除非字段数很大),这种设计无论如何都尽可能地高效,并且更容易编码。
我将使用Hibernate称为Table-per-subclass的表,因此我的数据库将为Box添加3个表:Box,ShippingBox和ReturnBox。 BoxItem中的FK将指向Box。