【问题标题】:Is it possible to constrain a table to have a value in only one of a set of columns是否可以将表限制为仅在一组列中的一个中具有值
【发布时间】:2009-09-11 01:20:16
【问题描述】:

我有一个表需要链接三个单独的表之一,但它应该只链接到其中一个,例如

Main_Table
id UNIQUEIDENTIFIER
t1_id UNIQUEIDENTIFIER
t2_id INT
t3_id INT

T1
id UNIQUEIDENTIFIER
name VARCHAR(255)

T2
id INT
name VARCHAR(255)

T3
id INT
name VARCHAR(255)

是否有可能有一个约束条件,即任何时候 t1、t2 或 t3 中的一个都不为空?

这只是糟糕的设计吗?如果是,您对设计有何建议?

编辑:

有人要求我详细说明这种特殊设计背后的原因。

Main_Table 试图成为付款人表,它可以引用单个用户 (T1)、一组个人用户 (T2) 或一组组 (T3)。

这是我继承的数据库设计,很遗憾,它不会真正改变。

我最大的问题是我需要在不同类型之间进行关联,所以类型字段在这里不起作用,因为索引不同。

【问题讨论】:

  • 它闻起来很糟糕。您能否更坦率地了解数据是什么以及为什么要以这种方式关联数据?
  • 看起来您正在尝试创建某种条件外键。正如 spender 所说,了解您想要完成的工作将有助于我们确定最佳方法。

标签: tsql database-design constraints


【解决方案1】:

您所描述的设计称为独家弧。是的,这是一个非常脆弱的设计,甚至不符合一些规范化规则。

这里有一个替代方案:

Main_Table
id UNIQUEIDENTIFIER
t_id INT NOT NULL
  FOREIGN KEY (t_id) REFERENCES T0 (id)

T0
id UNIQUEIDENTIFIER
type INT NOT NULL CHECK (type IN (1,2,3))
  UNIQUE KEY (id, type)

T1
id INT 
type INT NOT NULL CHECK (type = 1) 
name VARCHAR(255)
  FOREIGN KEY (id, type) REFERENCES T0 (id, type)

T2
id INT
type INT NOT NULL CHECK (type = 2)
name VARCHAR(255)
  FOREIGN KEY (id, type) REFERENCES T0 (id, type)

T3
id INT
type INT NOT NULL CHECK (type = 3)
name VARCHAR(255)
  FOREIGN KEY (id, type) REFERENCES T0 (id, type)

使用这种设计,Main_Table 中的每一行都必须引用 T0 中的一行。
同样,T0 中的每一行只能是 T1T2T3 中一行的父级。

这是一种在不破坏参照完整性的情况下实现类表继承和多态关联的方法。


Main_Table 正在尝试成为付款人 表,它可以引用任一 个人用户(T1),一组 个人用户 (T2) 或一组 组 (T3)。

是的,所以从面向对象设计的角度来考虑这一点。如果您有三个可以充当付款接收者的类,您将创建一个名为Payable 或其他名称的接口,以便每个您都可以依赖输入这些对象。例如,所有Payable 对象都必须有一个sendPayment() 方法。在一些 OO 语言中,接口是超类,被称为抽象类纯虚拟类

T0 表作为每个子表 T1T2T3 的通用类型。当Main_Table 有一个指向T0 的外键时,这就像是说Main_Table 必须引用某个实体Payable,但任何从该超类派生的对象都可以使用。

type 列只是确保给定T0.id 一次只能由一个子类表引用的技巧。如果您可以依靠您的应用程序逻辑将给定的子行仅插入其中一个子类表中,这是一种可选的。


另请参阅我的演示文稿“SQL Antipatterns Strike Back”中关于多态关联的部分。

【讨论】:

  • 我不明白为什么这个例子中的 T0 是必要的?这并不能解决不同类型密钥的问题。我只是在这里遗漏了什么吗?
  • @Bill:这是一个很棒的演示。我想我只需要使用代理来处理使用不同键查找信息。感谢您的帮助!
【解决方案2】:

如果您的数据库有检查约束,您可能会弄出一个丑陋的杂物,例如:

ALTER TABLE Main_Table
 add constraint CK_ThisWorksButItsUgly
  check ( (  case when t1_id is null then 0 else 1 end
           + case when t2_id is null then 0 else 1 end
           + case when t3_id is null then 0 else 1 end) = 1)

那里的一些语法可能是错误的,但你明白了。它可能会表现得足够好——只有当其中一列被修改时才会触发检查——但它绝不是漂亮的。

Bill Karwin 的 Exlusive Arc 非常酷,如果您可以重新设计数据库设计。

【讨论】:

    【解决方案3】:

    除了糟糕的设计,如果你不能改变它,可以使用触发器来强制执行这个约束

    【讨论】:

      【解决方案4】:

      这是 gen-spec 模式的又一个实例。

      查看有关“泛化专业化关系建模”的网络文章。那里有一些优秀的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-18
        • 1970-01-01
        • 2021-10-29
        • 1970-01-01
        相关资源
        最近更新 更多