【问题标题】:Database design circular reference / potential inconsistency数据库设计循环参考/潜在的不一致
【发布时间】:2018-07-11 05:20:32
【问题描述】:

我想不出办法来消除这种潜在的不一致:

考虑这 4 个实体:

     CUSTOMERS  >----------------   LOCATIONS
      id                             id
      name                           name
      loc_id           
                                       |
       |                               |
       |                               |
       |                               |
       /\                              /\

      ORDERS   >------------------   PRODUCTS           
       c_id                           id
       p_id                           name
                                      loc_id

每个客户都有一个位置(id:“3”,名称:“US”)

每个客户订购许多产品,每个产品可以被许多客户购买(这些交易存储在 ORDERS 表中)

每个产品只提供给具有相同位置的客户 (“美国”客户可以使用“美国”产品)

问题:

我想让 product 仅对“美国”的 customers 可用,因此我插入了一个 loc_id 为“3”的 product(美国)。

当我插入一个 loc_id 为“2”(欧盟)的 customer 并为同一 product 创建一个 order 时,问题就出现了。我最终在一个 LOCATION 找到了一个客户,他订购了在另一个 LOCATION产品 /强>。有什么办法可以避免吗?

【问题讨论】:

  • 听起来你需要一个触发器。
  • 查看我对How to apply complex constraints to a database table in MySQL? 的回复。但是,您的案例可能要求客户的声誉级别必须大于或等于产品的声誉级别,而不仅仅是等于。这需要一个触发器。
  • @reaanb 我只需要它们匹配。我将不得不花一些时间来分析您的链接,但据我所见,我可以将 replvl_id 外键添加到我的 ORDERS 表中,该表引用 CUSTOMERS 和 PRODUCTS,并使用复合键强制执行我的要求。我在正确的轨道上吗?
  • @Danbisso 是的,但是有两个复合键。
  • 声誉相等的情况是一个常见问题。谷歌我的 cmets 关于谷歌搜索你问题的许多版本。你在评论中说这就是你想要的。那不是你的问题。请编辑您的问题以实际提出您要提出的问题。 (评论不是为了澄清。)

标签: mysql sql database database-design database-normalization


【解决方案1】:

当我插入一个 loc_id 为“2”(欧盟)的客户并为同一产品创建订单时,就会出现问题。我最终得到了一个在一个位置的客户,该客户在不同的位置订购了产品。有没有办法避免这种情况?

有两种方法可以添加此限制。在您的架构中,或在您的 model 中。

在架构中,您可能会在订单表上使用触发器来确保客户位置和产品位置相同。

在模型中,您需要向订单模型添加逻辑,以在创建订单之前验证客户位置和产品位置是否相同。并且您将确保创建订单的所有内容都使用该模型,而不是随意进行 SQL 查询。出于其他维护原因,您也想要这个。

放在哪里?模型还是模式?我的答案是模型。这是我的想法...

Schema 用于数据完整性。模型用于业务规则。

推论:“业务规则”包括大多数数据验证。

“客户和订单必须有相同的位置”是一个业务规则。这是业务定义的限制,可以随时更改。

相比之下,诸如“每个产品都必须有一个有效的位置”或“您不能删除有订单的产品”之类的内容是数据完整性问题。如果products.loc_idnull 或者指向一排不存在的位置,就会发生不好的事情。这就是 not nullreferencescascade 之类的东西。

业务规则一直在变化,因此您需要一个快速且易于更改的解决方案。

架构成本高昂且更改风险很大。任何更改都可能影响使用数据库的所有内容。触发器是不透明且复杂的。 SQL 在它可以进行的验证方面也受到限制。检查相等性是一回事,但它可以检查 URL 是否有效吗?那句话不是脏话吗?有了这样的限制,您最终会面临在架构和模型之间拆分验证和业务规则的压力,从而冒着干扰和混淆的风险。

相比之下,模型易于更改和测试。它可以对业务规则的突发奇想做出快速反应。编程语言更适合全方位的数据验证,很多模型库都自带丰富的验证功能。有些甚至会自动将这些转换为您的架构约束。

业务规则和数据验证变得复杂

此外,就数据而言,业务规则是任意的外部限制。如果你把它放在架构中,所有与数据交互的东西都必须有这个限制:没有例外。这意味着您放入架构中的任何业务规则都必须处理所有可能性。

业务规则一开始往往很简单,但随着时间的推移会变得更加复杂。触发器和约束不是为复杂而设计的。将您的业务规则放入架构中往往会限制您的业务可能执行的操作,因为这些规则不能轻易地表达为触发器。

相比之下,模型是用能够很好地处理复杂性的编程语言编写的。也许美国和加拿大需要分组?还是欧盟国家?也许有些客户为国际运输支付了额外的费用?用任何体面的编程语言编写的模型都可以处理所有这些额外的复杂性。

如果不是所有东西都使用该模型怎么办?存储过程。

如今,我们倾向于编写模型并使用工具,然后这些工具会为我们编写架构。并且预计与数据库的所有交互都将通过模型进行。但情况并非总是如此。如果数据验证在模型中,那么使用数据库而不是模型的东西呢?

我的第一个答案是:考虑不这样做。如果异构系统需要访问数据,请考虑使用micro-service 作为数据库上的抽象层。然后其他需要使用数据的系统通过定义良好的 API 与之交互。这样可以避免每个人都无所适从地抓取数据库,而永远不知道谁在处理数据。

如果您无法做到这一点,请考虑使用存储过程的混合方法。编写存储过程作为模型的一部分。他们处理与数据库的交互并完成所有CRUD 的工作。在您的情况下,您将编写一个 new_order 存储过程,它会执行模型将执行的所有验证。然后任何与数据库交互的人都将使用存储过程,而不是编写自己的查询。

许多数据库甚至允许用其他语言编写存储过程,从而让您充分利用它们的灵活性。不幸的是 MySQL 没有。如果您刚刚开始进步,请考虑切换到更强大的数据库,例如 PostgreSQL。

结论

您所说的数据验证是一种业务规则。业务规则一直在变化,它们往往变得比触发器和约束所能处理的更复杂。它们属于更容易更改和更强大的验证工具的模型。

并确保有一个模型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-21
    • 2014-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多