在没有存储过程,触发器或UDF的情况下实施超类型和子类型数据完整性

ntw*_*twk 5 mysql sql database postgresql database-design

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

作为第一步,我提出了下面给出的逻辑模型.我对数据建模的了解有限,因此我的图表中可能存在错误,但希望它能传达我的意图.请注意,这仅仅是较大模式的简化部分,为简单起见,我仅提供相关表格.

数据库架构图

数据模型简要介绍

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

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

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

一些进一步的数据限制

  • 订单必须至少有一个关联的"订单商品"(即订单不能为空)
  • 订单商品必须是FoodItem或ComboItem的一个关联子类型(即,OrderItem不能同时是FoodItem和ComboItem).
  • 未来可能会出现一些进一步的限制因素

我希望将这些数据完整性规则融入到数据库中,这样我就不必担心自己在每个新客户端应用程序中执行它们,抛弃脚本或我写的伪劣SQL语句.我怀疑,如果我不在数据库级别做出这些保证,那么我将极大地增加遇到数据完整性问题的可能性.

这个问题

我对存储过程,触发器和用户定义的函数只有最微弱的了解.我得到的印象是,部分或全部这些功能可以帮助我实现我想要的目标.但是,如果我能完成检查约束,外键和相对简单的功能,我很乐意走这条路.基本上,我希望尽可能地限制复杂性,如果没有保证,不要引入每个漂亮的数据库功能.是否有可能确保我想要的数据完整性,而无需借助存储过程,触发器,用户定义的函数和其他更深奥的数据库功能?

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

最后,如果这种对数据完整性的方法被认为是过度的,或者如果有一个更实用但稍微不完美的解决方案,我也会对此持开放态度.

Bra*_*vic 2

不幸的是,“现代”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如您已经指出的,例如排他性和子女的存在,这对于继承很重要。

4 PostgreSQL 支持但 MySQL 不支持。

5按优先顺序排列:

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

6但要警惕竞争条件:事务隔离会保护您免受其中一些条件的影响,但不是全部,并且您可能需要执行一些显式锁定。