【问题标题】:Enforcing supertype & subtype data integrity without stored procedures, triggers, or UDFs在没有存储过程、触发器或 UDF 的情况下强制执行超类型和子类型数据完整性
【发布时间】:2014-06-12 08:50:59
【问题描述】:

我经营一家小型食品生产企业,我需要管理客户订单。我已经为我的业务的这方面建立了一个概念数据模型,但我需要一些关于如何在 RDMS 中完全实现它的指针。

作为第一步,我想出了下面给出的逻辑模型。我的数据建模知识有限,所以我的图表中可能存在错误,但希望它传达了我的意图。请注意,这只是较大架构的简化部分,为简单起见,我仅提供相关表。

数据模型简介

  • 一个客户 Ordr 可以有一个或多个 OrdrItems
  • OrdrItem 可以是 FoodItem 或 ComboItem
  • ComboItem 是两个或多个 FoodItem 的逻辑分组

我已经在 MySQL 中实现了上述模式,并编写了一些小程序来用客户订单填充表。这可以完成工作,但没有太多考虑数据完整性。我注意到,通过这种实现,一些数据完整性规则并未在数据库级别强制执行。

例如,FoodItem 是 OrdrItem 的子类型。对于 FoodItem 中的每一行,OrdrItem 中必须恰好有一个对应的行。但是,在其当前实现中,我可以从 FoodItem 中删除一行,从而在 OrdrItem 中留下一行,而在其中一个子类型表中没有相应的行。这应该是不允许的。

一些进一步的数据限制

  • 订单必须至少有一个关联的“订单项”(即订单不能为空)
  • 订单项必须是 FoodItem 或 ComboItem 的一个关联子类型(即 OrderItem 不能同时是 FoodItem 和 ComboItem)。
  • 未来可能会出现一些进一步的限制

我希望将这些数据完整性规则纳入数据库,这样我就不必担心它们在我编写的每个新客户端应用程序、一次性脚本或伪劣 SQL 语句中的执行情况。我怀疑如果我不在数据库级别做出这些保证,我会大大增加我以后遇到数据完整性问题的机会。

问题

我对存储过程、触发器和用户定义的函数只有最基本的了解。我的印象是这些功能中的一些或全部可以帮助我实现我想要的。但是,如果我可以单独使用检查约束、外键和相对简单的功能来完成工作,我会很乐意走这条路。基本上,我想尽可能地限制复杂性,如果没有必要,不要引入每一个漂亮的数据库功能。是否可以在不借助存储过程、触发器、用户定义函数和其他更深奥的数据库功能的情况下确保我想要的数据完整性?

我愿意使用 MySQL 或 Postgresql 来实施我的解决方案,因为我对这两个系统都有基本的工作知识。

最后,如果这种处理数据完整性的方法被认为是矫枉过正,或者如果有更实用但稍微不完善的解决方案,我也愿意接受。

【问题讨论】:

  • 绝对不是标准的订单数据模型。标准订单数据模型将具有一种类型的 order_item,它将引用不同的产品类型,例如食物或组合。
  • +1 写得很好的问题。简短的回答通常是肯定的,但可能不是您当前的模式——那些 (ordrID, OrdrItemID) 元组不是一个好主意,并且字段名 fkey 是彻头彻尾的可怕。我希望你能找到一个志愿者来扩展这个(和那个,可能作为单独的问题)。如果不是(即使如此),我认为您应该寻求一些专业支持,至少可以帮助您正确地建模您的架构。
  • @Denis,感谢您在我的架构中发现问题。经过仔细检查,我发布的原始架构图有许多错误,主要涉及各种表中不应该存在的列。我已经把东西清理干净了,希望它看起来更理智一些。

标签: mysql sql database postgresql database-design


【解决方案1】:

不幸的是,“现代”DBMS 并不直接支持您可以放入 ER 图中的所有花哨符号1。物理 FOREIGN KEY 实际强制执行的所有内容是子行不能没有父行存在,这为您提供了一个简单的“1 到 0 或 N” 关系2

你可以...

  • 使 FK NULL 能够将关系的左侧变形为“0 或 1”
  • 和/或您可以在 FK 顶部放置一个键以将右侧变形为“0 或 1”

...但这就是您“开箱即用”所能做的所有事情。

要强制执行其他规则3,您要么必须显着“丑化”模型并可能使用延迟约束4,要么您可以这样做程序代码5.

虽然您对将尽可能多的完整性规则放入数据库本身的直觉是正确,但在程序代码中强制执行“不寻常”情况而不是将数据模型扭曲成椒盐卷饼以适应声明性约束的限制。

事实上,最流行的技术之一是创建“API”:

  • 禁止客户端直接修改表(通过撤销适当的权限)
  • 并允许他们仅通过您编写的存储过程修改数据,这些存储过程强制执行所有必要的业务规则6。这样一来,您就可以通过同一个“票据交换所”汇集所有客户,并且没有人可以行为不端。

不过,这是一个相当“繁重”的解决方案,如果场景足够简单,可能不值得麻烦。如果您的应用程序是唯一会修改数据库的应用程序,那么只需在客户端代码中实现规则就足够了...


1 如继承(又名子类​​型、类别、泛化层次结构)。

2 左侧:任何给定的孩子必须有“1”父母。右侧:任何给定的父级必须有“0 或 N”个子级。

3 比如exclusivity and presence of children,这对继承很重要,你已经注意到了。

4 PostgreSQL 支持但 MySQL 不支持。

5 按优先顺序排列:

  • 触发器和存储过程
  • 中层
  • 或客户。

6 但是要小心竞争条件:transaction isolation 会保护您免受其中一些的影响,但不是全部,您可能需要进行一些显式锁定。

【讨论】:

  • @a_horse_with_no_name 不,这种“排他性”是另一回事。当实现为“每个类在自己的表中”时,它与继承有关。在这种情况下,一个“逻辑对象”由两行表示:一个在父表中,一个在一个子表中。不同子表中不应有多行引用父表中的同一行。相关的概念是“存在”,这意味着父项中不能有行而子项中没有行。请参阅脚注 3 中的链接,了解以声明方式强制执行这两项操作的方法。
  • @a_horse_with_no_name 正如我之前提到的,PostgreSQL 继承没有完全实现,使得它的实用性相当有限。
猜你喜欢
  • 2010-09-18
  • 1970-01-01
  • 1970-01-01
  • 2016-05-19
  • 1970-01-01
  • 1970-01-01
  • 2023-03-30
  • 2012-03-13
  • 2018-09-26
相关资源
最近更新 更多