【问题标题】:How to prevent orphaned polymorphic records?如何防止孤立的多态记录?
【发布时间】:2022-11-24 05:10:22
【问题描述】:

我有一个多态结构的数据库:一个“基本”类型表和两个“派生”类型:

CREATE TABLE ContactMethod(
  id integer PRIMARY KEY
  person_id integer
  priority integer
  allow_solicitation boolean 
  FOREIGN KEY(person_id) REFERENCES People(id)
)

CREATE TABLE PhoneNumbers(
  contact_method_id integer PRIMARY KEY
  phone_number varchar
  FOREIGN KEY(contact_method_id) REFERENCES ContactMethod(id)
)

CREATE TABLE EmailAddresses(
  contact_method_id integer PRIMARY KEY
  email_address varchar
  FOREIGN KEY(contact_method_id) REFERENCES ContactMethod(id)
)

我想防止孤立的 ContactMethod 记录存在,即 ContactMethod 记录既没有相应的 PhoneNumber 记录也没有 EmailAddress 记录。我见过确保排他性的技术(防止 ContactMethod 记录同时具有相关的 PhoneNumberEmailAddress),但不是为了防止孤儿。

一个想法是 CHECK 约束,它执行执行查询的自定义函数。但是,通过 CHECK 约束中的函数执行查询不是一个好主意。

另一个想法是如果添加孤立的 ContactMethod 记录,将触发违规的视图。做到这一点的“明显”方法是对视图施加约束,但这是不允许的。所以它必须是某种技巧,可能涉及视图上的索引。这真的是最好的(唯一的?)强制没有孤儿的方法吗?如果是这样,什么是工作示例?

还有其他方法吗?我可以摆脱 ContactMethod 表和其他两个表上的重复共享列,但我不想那样做。我主要对 MySQL 和 SQLite 中可用的功能感到好奇,但是任何 SQL 引擎中的解决方案都会有所帮助。

【问题讨论】:

  • 1) 从数据库设计的角度来看,没有电话号码和电子邮件地址行的 ContactMethod 行不是孤立的。如果人员 ID 字段为空或指向无效的父值,则 ContactMethod 行将是孤立的。正确的术语是没有孩子,而不是孤儿。 2)外键只能以相反的方式工作。 sql 中没有标准解决方案来处理此类情况。因此,您依赖于通过触发器和存储过程实现自定义代码。这些解决方案特定于您使用的数据库产品,您需要将问题缩小到一种数据库产品。

标签: mysql sql sqlite database-design class-table-inheritance


【解决方案1】:

新插入的 ContactMethod 将始终处于孤立状态,直到您插入电话号码或电子邮件地址。因此,您无法在插入时测试条件。

相反,您可以使用除了基本信息之外还具有可选电话号码和可选电子邮件参数的存储过程来插入联系信息。仅当两者中至少有一个具有非空值时,才会插入基本记录。

然后在删除电话号码或电子邮件地址时创建删除触发器,以在不再存在相关记录时删除ContactMethod记录或引发异常,如Alter a Delete Trigger to Check a Column Value所示

【讨论】:

    【解决方案2】:

    最简单的解决方案是使用单表继承。因此,这两种联系方法都是 ContactMethod 表中的可选(即可为空)字段,但您添加了一个 CHECK 约束以确保其中至少有一个具有非空值。

    CREATE TABLE ContactMethod(
      id integer PRIMARY KEY
      person_id integer
      priority integer
      allow_solicitation boolean,
      phone_number varchar DEFAULT NULL
      email_address varchar DEFAULT NULL 
      FOREIGN KEY(person_id) REFERENCES People(id)
      CHECK (COALESCE(phone_number, email_address) IS NOT NULL)
    )
    

    另一种支持多态关联的解决方案是反转外键的方向。使 ContactMethod 对于每种类型的关联方法都有一个可以为 null 的外键。使用 CHECK 确保至少有一个具有非空值。这是有效的,因为您不允许在 ContactMethod 中每行有多个电子邮件或电话。这确实意味着如果您添加不同类型的联系人(例如 Signal 帐户),那么您必须向该表添加另一个外键。

    CREATE TABLE ContactMethod(
      id integer PRIMARY KEY
      person_id integer
      priority integer
      allow_solicitation boolean,
      phone_number_id integer DEFAULT NULL
      email_address_id integer DEFAULT NULL 
      FOREIGN KEY(person_id) REFERENCES People(id)
      FOREIGN KEY(phone_number_id) REFERENCES PhoneNumbers(id)
      FOREIGN KEY(email_address_id) REFERENCES EmailAddresses(id)
      CHECK (COALESCE(phone_number_id, email_address_id) IS NOT NULL)
    )
    

    【讨论】:

      猜你喜欢
      • 2010-10-01
      • 1970-01-01
      • 2018-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多