【问题标题】:Conditional foreign key in SQLSQL中的条件外键
【发布时间】:2023-03-24 09:47:02
【问题描述】:

我有一个名为 PartyChannel 的表,其中包含以下列

 ID, ChannelID, ChannelType

ChannelID 存储 MailIDPhoneIDEmailID,具体取决于 ChannelType

那么如何根据 channelType 在 PartyChannel 和所有三个表(Mail、Email 和 Phone)之间创建外键。

【问题讨论】:

  • MailID、PhoneID、EmailID 是三个不同的域,不能也不应该使用单个 ChannelID 列进行建模。这是基本的东西。

标签: sql-server sql-server-2008 database-design


【解决方案1】:

您可以将 PERSISTED COMPUTED 列与 case 语句一起使用,但最终,它只会给您带来开销。

最好的解决方案是将它们建模为三个不同的值。

CREATE TABLE Mails (MailID INTEGER PRIMARY KEY)
CREATE TABLE Phones (PhoneID INTEGER PRIMARY KEY)
CREATE TABLE Emails (EmailID INTEGER PRIMARY KEY)

CREATE TABLE PartyChannel (
  ID INTEGER NOT NULL
  , ChannelID INTEGER NOT NULL
  , ChannelType CHAR(1) NOT NULL
  , MailID AS (CASE WHEN [ChannelType] = 'M' THEN [ChannelID] ELSE NULL END) PERSISTED REFERENCES Mails (MailID)
  , PhoneID AS  (CASE WHEN [ChannelType] = 'P' THEN [ChannelID] ELSE NULL END) PERSISTED REFERENCES Phones (PhoneID)
  , EmailID AS  (CASE WHEN [ChannelType] = 'E' THEN [ChannelID] ELSE NULL END) PERSISTED REFERENCES Emails (EmailID)
)

免责声明

仅仅因为你可以并不意味着你应该。

【讨论】:

  • 这很疯狂。它适用于所有主要的 SQL 实现吗?
  • @Mark:它适用于 SQL Server 2005 和 2008。除此之外,我真的不知道,这是一个很好的心理练习 。但是,免责声明可能是正确的:仅仅因为你可以并不意味着你应该!
  • 它确实在 SQL Server 中工作,尽管您的 ChannelType 应该是 char 或 varchar 而不是 int。再次 - +1 - 谢谢。非常有趣和不寻常的东西。我仍然同意@onedaywhen 这不是一个好的设计,但我很欣赏你提供的另类 SQL 课程!
  • 糟糕 - 刚刚看到您的评论。 RE:“只是因为你可以......” - 你说得对。真正让我惊讶的是,您可以使用 SQL 做多少事情。
  • @Mark,感谢您发现这一点。我已经修复了 ChannelType 类型。
【解决方案2】:

子类型 Email, Mail, PhoneChannel

【讨论】:

  • 这是 gen-spec 模式,在关系模型中表示。该图很好地说明了这些键。干得好。
【解决方案3】:

AFAIK,您不能使用标准外键执行此操作。但是,您可以通过使用触发器来实现一些东西来帮助确保数据完整性。本质上,只要在引用表上有插入或更新,触发器就会检查被引用表上是否存在“外键” - 必须存在的值。同样,从被引用表中删除可能有一个触发器,用于检查引用表上使用被删除键的记录。

更新:虽然我选择了“答案”,但我同意@onedaywhen 留下的评论,认为这实际上是一个由设计引起的问题,可能会让你重新考虑你的设计。也就是说,您应该拥有三个不同的列,而不是一列引用三个表。当一列被填充时,您只需将其他两列保留为空,这反过来又可以让您使用标准外键。任何担心这会“占用太多空间”都是愚蠢的。它代表了过早优化的严重案例——这无关紧要。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-22
    • 1970-01-01
    • 2016-10-07
    相关资源
    最近更新 更多