如何实现这种多表数据库设计/约束,规范化?

时间:2010-02-02 16:50:43

标签: sql sql-server database normalization

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

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 ID配对。还有一个约束条件是Real ID不能在单个类中重复出现。如何在一个连贯的设计中捕获所有这些?

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

  1. 它可以为空(有些时候我们不知道某事物的Real ID应该是什么。)
  2. 这是更多数据的外键。
  3. 显然,这可以通过充当约束的触发器来完成,但我想知道这是否可以使用常规约束/唯一索引来实现。使用 SQL Server 2005

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

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

    编辑^ 2:如下面的评论所示,我似乎暗示外键不能为空。哪个是假的,他们可以!但是,无法做到的是在NULL重复的字段上设置唯一索引。尽管唯一索引支持NULL值,但它们不能约束集合中的多个NULL。由于Real ID赋值最初是稀疏的,因此每个类的多个NULL Real ID更有可能。

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

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

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

7 个答案:

答案 0 :(得分: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表中根本不需要Class;它应该只存在于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列。

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

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

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

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

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

我无法看到完美无冗余的设计。

我认为Elements_Xref表应该包含:

  • 综合ID
  • 真实身份

with(Synthetic ID,Class)作为引用Elements的'外键',Real ID上的NOT NULL约束,以及(Class,Real ID)上的唯一约束。

Elements_Xref表仅包含已知Real 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)
);

答案 1 :(得分:1)

你可以

  1. Elements_Xref

  2. 上一起创建加入ElementsSynthetic ID的结果集中的视图
  3. class[Real ID]上添加唯一约束。在其他新闻中,这也是通过索引视图在MSSQL中执行功能索引的方式。

  4. 这是一些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

    这是Microsoft的解决方法:

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

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

答案 2 :(得分:1)

我会:

  1. 在Elements(Synthetic ID,Class)上创建一个UNIQUE约束
  2. 将类列添加到Elements_Xref
  3. 在Elements_Xref表上添加FOREIGN KEY约束,参考(Synthetic ID,Class)
  4. 此时我们确信Elements_Xref.Class始终与Elements.Class匹配。

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

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

答案 3 :(得分:1)

为Elements_Xref创建索引视图,其中Real_Id不为空,然后在该视图上创建唯一索引

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 :(得分:0)

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

答案 5 :(得分:0)

我认为您的两个原因中的任何一个都不会将Real ID放入Elements。如果给定元素具有0或1 Real ID s(但绝不超过1),则它应该绝对位于Elements表中。这样就可以限制Class内的唯一性(我认为)。

你可以扩展两个不这样做的理由吗?

答案 6 :(得分:0)

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

这会将实际ID限制为类的唯一,并为您提供一种方法,将类和实际ID与合成ID匹配

至于Real ID是外键,你的意思是如果它是两个类,那么键入它的数据将是相同的。如果是,则添加具有键Real Id的另一个表。这个密钥是一个外键到real_elements和任何其他需要真实ID作为外键的表