【问题标题】:Database Design - How to enforce a foreign key constraint on a table with a Surrogate Key数据库设计 - 如何对具有代理键的表实施外键约束
【发布时间】:2010-09-30 06:41:39
【问题描述】:

我的表架构是这样的

1.主表:子句

  1. ClauseID = 代理 pk(身份)
  2. ClauseCode = nvarchar 用户指定值
  3. Class = nvarchar FK 到主类表
  4. 等等……

ClauseCode + Class = 此表的候选键

2。主表:GroupClause

  1. GroupClauseID = 代理 pk(身份)
  2. GroupClauseCode = nvarchar 用户指定值
  3. Class = nvarchar FK 到主类表 等等……

GroupClauseCode + Class = 此表的候选键

3。事务/映射表 :: GroupClause_Clause_Mapping :该表将每个组子句映射到多个单独的子句

  1. GroupClauseID = FK 到 GroupClause PK
  2. ClauseID = FK 到条款 PK
  3. 等等……

要求:每个Group子句只能映射到与自己属于同一类的子句

问题:上表设计并未在数据库级别强制执行该要求。

一种可能的解决方案: *GroupClause_Clause_Mapping* 表有列

  1. 条款代码
  2. GroupClauseCode

其中我可以创建 ClauseCode + Class 作为 FK 到子句表以及 GroupClauseCode + 类作为 GroupClause 表的 FK。

但是,如果我这样做,那么代理身份密钥就没用了,我还不如摆脱它们

使用代理键的设计是否存在问题?

关于如何使用代理键并仍然在数据库级别强制执行我的约束的任何建议?

【问题讨论】:

    标签: database-design foreign-keys surrogate-key


    【解决方案1】:

    如果代码本身很大/笨拙,那么您可能仍想使用代理(如果可用)。

    既然你只想强制类匹配,那么你的映射表可以是

    ClauseID,
    GroupClauseID,
    Class (or possibly ClassID)
    

    对于您的主表,您仍然有 PK (ClauseID) 和唯一约束 (ClauseID, Class)。然后,您可以决定是只使用 FK (ClauseID,Class) 还是在映射表和每个主表之间有两个 FK(实际上,您会说一个 FK 是外键引用,另一个用于执行你的规则)。

    我的一个数据库中有一个类似的设置(想想调查系统):

    CREATE TABLE [dbo].[DataItems](
        [DataItemID] [uniqueidentifier] ROWGUIDCOL  NOT NULL,
        [TypeRequired] [varchar](10) NOT NULL,
        [Name] [varchar](50) NOT NULL,
        /* Other Columns */
     CONSTRAINT [PK_DataItems] PRIMARY KEY NONCLUSTERED 
    (
        [DataItemID] ASC
    ),
     CONSTRAINT [UX_DataItems_ClientAnswerFKTarget] UNIQUE CLUSTERED 
    (
        [DataItemID] ASC,
        [TypeRequired] ASC
    ),
     CONSTRAINT [UX_DataItems_Name] UNIQUE NONCLUSTERED 
    (
        [Name] ASC
    )
    )
    
    CREATE TABLE [dbo].[ClientAnswers](
        [ClientAnswersID] [uniqueidentifier] ROWGUIDCOL  NOT NULL,
        [ClientID] [uniqueidentifier] NOT NULL,
        [DataItemID] [uniqueidentifier] NOT NULL,
        [TypeRequired] [varchar](10) NOT NULL,
        [BoolValue] [bit] NULL,
        [IntValue] [int] NULL,
        [CharValue] [varchar](6500) NULL,
        [CurrencyValue] [int] NULL,
        [DateValue] [datetime] NULL,
        /* Other Columns */
     CONSTRAINT [PK_ClientAnswers] PRIMARY KEY CLUSTERED 
    (
        [ClientID] ASC,
        [DataItemID] ASC
    )
    )
    GO
    ALTER TABLE [dbo].[ClientAnswers] ADD  CONSTRAINT [FK_ClientAnswers_DataItems] FOREIGN KEY([DataItemID],)
    REFERENCES [dbo].[DataItems] ([DataItemID])
    ON UPDATE CASCADE
    GO
    ALTER TABLE [dbo].[ClientAnswers] ADD  CONSTRAINT [FK_ClientAnswers_DataItems_TypesMatch] FOREIGN KEY([DataItemID],TypeRequired)
    REFERENCES [dbo].[DataItems] ([DataItemID],TypeRequired)
    GO
    

    然后我更进一步,有更多的约束确保类型列与非空 *Value 列匹配

    【讨论】:

    • 砰!在主表子句本身上放置一个唯一约束(ClauseID,Class),以便我可以从事务表中对其进行 FK 将起作用!我脑后有个小声音说这似乎有点误导,因为那个唯一约束的类部分实际上是多余的——你不会这么说吗?
    • @In Sane - 这在一定程度上是多余的。它只是一个超级键,它涵盖的列比唯一标识行所必需的列更多。但它当然不是多余的,因为它允许您表达更多的完整性规则。
    • 是的,你完全正确!我将按照您的建议进行!谢谢!
    【解决方案2】:

    “如果我这样做,那么代理身份密钥就没用了,我还不如摆脱它们。”

    没错。这是使用自然键而不是代理项的原因之一:当您需要使用自然键值实现一些额外的约束或逻辑时。

    您可能希望引用其他表中的代理项,以便代理项键仍然有用。如果您根本不使用代理,那么最好放弃它。代理键通常意味着一定的开销,因为它们通常是索引的,这会损害插入的性能。

    【讨论】:

    • 谢谢!我明白你的意思了。代理的原因是我可以在数字字段而不是字符串上进行连接/搜索等 - 用户定义的代码本质上是文本的,我希望避免在查询中使用它们。根据你的建议,我想我将不得不选择一个或另一个......这就是我希望它不会出现的:-)
    • 除非 char 键很宽,否则不会比 int 或数字键慢。
    猜你喜欢
    • 2016-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-09
    • 1970-01-01
    • 2015-05-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多