【问题标题】:How do I implement this multi-table database design/constraint, normalized?如何实现这个多表数据库设计/约束,规范化?
【发布时间】:2011-01-12 06:07:32
【问题描述】:

我的数据有点像这样......

Elements
Class | Synthetic ID (pk)
A     | 2
A     | 3
B     | 4
B     | 5
C     | 6
C     | 7

Elements_Xref
ID (pk) | Synthetic ID | Real ID (fk)
.       | 2            | 77-8F         <--- A class
.       | 3            | 30-7D         <--- A class
.       | 6            | 21-2A         <--- C class
.       | 7            | 30-7D         <--- C class

所以我有这些元素被分配了合成 ID 并被分组到类中。但是这些合成 ID 然后与我们真正关心的 Real IDs 配对。还有一个约束是 Real ID 不能在单个类中重复出现。如何在一个连贯的设计中捕捉所有这些?

我不想将Real ID 塞进上表,因为

  1. 它可以为空(有时我们不知道某事物的 Real ID 应该是什么)。
  2. 它是更多数据的外键。

显然,这可以通过充当约束的触发器来完成,但我想知道这是否可以通过常规约束/唯一索引来实现。使用 SQL Server 2005

我考虑过拥有两个主表 SyntheticByClassRealByClass,然后将这些表的 ID 放入另一个外部参照/链接表中,但这仍然不能保证两个元素的类匹配。也可以通过触发器解决。

编辑:这是关键字填充,但我认为它与规范化有关。

Edit^2:如下面的 cmets 所示,我似乎暗示外键不能为空。这是错误的,他们可以!但是不能做的是在 NULL 重复的字段上设置唯一索引。尽管唯一索引支持 NULL 值,但它们不能在一个集合中约束多个 NULL。由于 Real ID 分配最初是稀疏的,因此每个类很可能有多个 NULL Real IDs。

编辑^3:删除了多余的Elements.ID 列。

编辑^4:一般观察。似乎有三种主要的方法在起作用,我已经提到了其中一种。

  1. 触发器。使用触发器作为约束来中断任何会破坏数据完整性的数据操作。
  2. 索引连接表的视图。太棒了,我不知道您可以使用视图和索引来做到这一点。
  3. 创建一个多列外键。 没想到这样做,不知道这是可能的。将Class 字段添加到Xref 表中。在 (Class + Real ID) 上创建一个 UNIQUE 约束,在 (Class + Synthetic ID) 上创建一个外键约束,返回到Elements 表。

【问题讨论】:

    标签: sql sql-server database normalization


    【解决方案1】:

    问题之前的评论变成了“奖励”问题

    您希望能够表达 Elements 和 Elements_Xref 的连接对 Class 和 Real ID 具有唯一约束。如果你有一个支持 SQL-92 ASSERTION 约束的 DBMS,你可以做到。

    AFAIK,没有 DBMS 支持它们,所以你只能使用触发器。

    设计并没有将 Real ID 限制为跨类唯一,这似乎很奇怪;从讨论来看,一个给定的 Real ID 似乎可以是几个不同类的一部分。如果真实 ID 是“唯一的,除非为空”,那么如果 DBMS 支持“唯一的,除非为空”的概念(大多数不支持;我相信有一个,但我忘记了),那么您将能够更轻松地执行唯一性它是)。


    2010-02-08 修改前的评论

    该问题排除了“干扰”上表(元素)中的 Real_ID;不排除在下表(Elements_Xref)中包含 Class,然后允许您在 Elements_Xref 中的 Class 和 Real_ID 上创建唯一索引,实现(我相信)所需的结果。

    从样本数据中不清楚 Elements 表中的合成 ID 是否唯一,或者它是否可以在不同的类中重复(或者,合成 ID 是否可以在单个类中重复)。鉴于似乎有一个 ID 列(可能是唯一的)以及 Synthetic ID 列,假设有时合成 ID 重复似乎是合理的 - 否则表中没有很好的理由有两个唯一列。在大多数情况下,这无关紧要 - 但如果将类复制到 Elements_Xref 表,它确实会影响唯一性约束。还有一种可能;也许元素表中根本不需要类;它应该只存在于 Elements_Xref 表中。我们没有足够的信息来判断这是否可能。


    对 2010-02-08 所做更改的评论

    现在 Elements 表以 Synthetic ID 作为主键,事情就简单多了。有评论说“班级”信息实际上是“月”,但我会尽量忽略它。

    在 Elements_Xref 表中,我们有一个唯一的 ID 列,然后是一个 Synthetic ID(它没有标记为 Elements 的外键,但可能实际上必须是一个)和 Real ID。我们可以从样本数据中看到,多个 Synthetic ID 可以映射到给定的 Real ID。不清楚为什么 Elements_Xref 表同时具有 ID 列和 Synthetic ID 列。

    我们不知道单个 Synthetic ID 是否只能映射到单个 Real ID,或者它是否可以映射到多个 Real ID 值。

    由于 Synthetic ID 是 Elements 的主键,我们知道一个 Synthetic ID 对应一个 Class。

    我们不知道 Synthetic ID 到 Real ID 的映射是否随时间变化(可能因为 Class 与日期相关),以及是否必须记住旧状态。

    我们可以假设表格被缩减到最低限度,并且每个表格中还有其他列,其内容与问题没有直接关系。

    问题说明 Real ID 是其他数据的外键,可以为 NULL。

    我看不到一个完美的非冗余设计。

    我认为 Elements_Xref 表应该包含:

    • 合成 ID
    • 真实身份

    将 (Synthetic ID, Class) 作为引用元素的“外键”,对 Real ID 使用 NOT NULL 约束,对 (Class, Real ID) 使用唯一约束。

    Elements_Xref 表仅包含已知真实 ID 的行 - 并正确执行所需的唯一性约束。

    奇怪的是 Elements_Xref 中的 (Synthetic ID, Class) 数据必须匹配 Elements 中的相同列,即使 Synthetic ID 是 Elements 的主键。

    在 IBM Informix Dynamic Server 中,您可以实现:

    CREATE TABLE elements
    (
        class CHAR(1) NOT NULL,
        synthetic_id SERIAL NOT NULL PRIMARY KEY,
        UNIQUE(class, synthetic_id)
    );
    
    CREATE TABLE elements_xref
    (
        class CHAR(1) NOT NULL,
        synthetic_id INTEGER NOT NULL REFERENCES elements(synthetic_id),
        FOREIGN KEY (class, synthetic_id) REFERENCES elements(class, synthetic_id),
        real_id    CHAR(5) NOT NULL,
        PRIMARY KEY (class, real_id)
    );
    

    【讨论】:

    • 类实际上是“月”。它是跨时间重复出现的数据。这种尴尬的设计是不得不处理不太理想的数据并将其与完美数据配对的副作用。
    • 我已更新 OP 以解决您的其他 cmets。第一个表的合成 ID 是 PK。我意识到它和 ID 是多余的。
    • @Mark Canlas:感谢您的更新。还有更多问题,但显示的设计至少在一个 DBMS 中有效,尽管由于 element_xref 中的双重交叉引用和元素中声称的“双键”(当更大的键只是一个实际主键的超级键)。不是很好 - 但它确实有效。
    【解决方案2】:

    我愿意:

    1. 在元素(合成 ID、类)上创建唯一约束
    2. 将类列添加到 Elements_Xref
    3. 在 Elements_Xref 表上添加 FOREIGN KEY 约束,引用(Synthetic ID, Class)

    此时我们确定 Elements_Xref.Class 始终与 Elements.Class 匹配。

    现在我们需要实现“非空时唯一”逻辑。点击链接并滚动到“使用计算列实现复杂的业务规则”部分: Indexes on Computed Columns: Speed Up Queries, Add Business Rules

    或者,您可以在 (Class, RealID) 上创建索引视图,并在其 WHERE 子句中使用 WHERE RealID IS NOT NULL - 这也将强制执行“非空时唯一”逻辑。

    【讨论】:

      【解决方案3】:

      使用 Where Real_Id Is Not Null 为 Elements_Xref 创建索引视图,然后在该视图上创建唯一索引

      Create View Elements_Xref_View With SchemaBinding As
      Select Elements.Class, Elements_Xref.Real_Id
      From Elements_Xref
      Inner Join Element On Elements.Synthetic_Id = Elements_Xref.Synthetic_Id
      Where Real_Id Is Not Null
      Go
      
      Create Unique Clustered Index Elements_Xref_Unique_Index
      On Elements_Xref_View (Class, Real_Id)
      Go
      

      这除了模拟一个正确处理空值的唯一索引(即 null != null )之外没有其他目的

      【讨论】:

        【解决方案4】:

        你可以

        1. 根据在Synthetic ID 上将Elements_XrefElements 连接在一起的结果集创建一个视图

        2. class[Real ID] 上添加唯一约束。在其他新闻中,这也是您在 MSSQL 中通过索引视图来执行功能索引的方式。

        这是一些sql:

        CREATE VIEW unique_const_view AS
        SELECT e.[Synthetic ID], e.Class, x.[Real ID]
        FROM Elements AS e
        JOIN [Elements_Xref] AS x
          ON e.[Synthetic ID] = x.[Synthetic ID]
        
        CREATE UNIQUE INDEX unique_const_view_index ON unique_const_view ( Class, [Real ID] );
        

        现在,显然,我自己不知道这个解决方案在 Microsoft-land-place 中不起作用,因为使用 MS SQL Server 重复空值将违反 UNIQUE 约束:这违反了 SQL 规范。 This is where the problem is discussed about.

        这是微软的解决方法:

        create unique nonclustered index idx on dbo.DimCustomer(emailAddress)
        where EmailAddress is not null;
        

        不确定那是 2005 年,还是 2008 年。

        【讨论】:

        • 如果我认为 Class 和 RealID 都存储在 Elements 表中,这会更好吗?您根本不需要交叉引用表,只需要 Elements 表和索引视图。
        • 根据问题:我不想将真实 ID 塞到上表中,因为它可以为空(在某些时期我们不知道某物的真实 ID 应该是什么)。它是更多数据的外键。
        • 我知道 OP 是这么说的,但是从他们所说的其他内容来看,我真的不明白将 RealID 放在 Elements 字段中会如何导致问题,即使它既可以为空又可以其他数据的外键。
        • 可以将 Real ID 放入 Elements 表中,但没有强制执行数据完整性。如果你要尝试向(Real ID + Class)添加索引,它会失败,因为会有很多 NULL Real ID。
        • 我已经更新了答案以显示 MS 与 SQL 规范的偏差。
        【解决方案5】:

        我认为触发器是您的最佳选择。约束不能交叉到其他表来获取信息。与唯一索引相同的事情(尽管我认为具有索引的物化视图可能是可能的),它们在表中是唯一的。将触发器放在一起时,请记住以基于集合的方式而不是逐行执行,并使用多行插入和多行更新进行测试,其中真实键在数据集中重复。

        【讨论】:

          【解决方案6】:

          我认为您的两个原因中的任何一个都不会成为将Real ID 放入Elements 的障碍。如果给定元素有 0 或 1 个 Real IDs(但永远不会超过 1),它绝对应该在 Elements 表中。然后,这将允许您限制 Class 内的唯一性(我认为)。

          您能否详细说明您不这样做的两个原因?

          【讨论】:

          • 呵呵,我以为外键不能为空...我现在正在测试它,它似乎可以工作...
          • 另外,即使我确实将 Real ID 放在第一个表中,我也无法使用索引强制执行约束,因为许多 NULL 被视为彼此重复。
          • 索引视图可以让您强制执行唯一性约束。是否只是因为您想强制实现 RealID/Class 的唯一性,而您认为不能将 RealID 放入 Elements 表中?
          【解决方案7】:

          创建一个新表 real_elements,其中包含 Real ID、Class 和 Synthetic ID 字段,主键为 Class、RealId,并在实际添加 RealID 时添加元素

          这将真实 ID 限制为对于一个类是唯一的,并为您提供一种将类和真实 ID 与合成 ID 匹配的方法

          至于 Real ID 是外键,你的意思是如果它在两个类中,那么键控它的数据将是相同的。如果是这样,请添加另一个带有键 Real Id 的表。然后,此键是 real_elements 和任何其他需要真实 ID 作为外键的表的外键

          【讨论】:

          • 这意味着您在知道 RealID 之前无法录制课程?我认为每个元素的 Class 都是已知的,即使 RealID 不是。
          • 没有信息类在 Elements 中,real_elements 中唯一的新数据是 Real ID
          • 啊,是的,我的错。但是像这样添加第二个带有 Class 和 SyntheticID 的表意味着我认为数据没有标准化?
          • 不,它是标准化的 - 类和合成 ID 是 Elements 的外键
          猜你喜欢
          • 2017-04-03
          • 2010-11-18
          • 2011-04-18
          • 2011-11-20
          • 1970-01-01
          • 2013-01-18
          • 2017-10-04
          • 2011-07-25
          • 2011-12-20
          相关资源
          最近更新 更多