【问题标题】:Uniqueness constraint on other columns in table of foreign key外键表中其他列的唯一性约束
【发布时间】:2018-06-26 13:08:27
【问题描述】:

我有一些 事物s 有 0-* 的名称,有任意数量的语言s:

CREATE TABLE Things(
    thing_id INT PRIMARY_KEY,
    thing_proprty INT);

CREATE TABLE ThingNames(
    thing_name_id INT PRIMARY_KEY,
    thing_id INT,
    language CHAR[2],
    name VARCHAR[64] UNIQUE,
    FOREIGN KEY (thing_id) REFERENCES Things(thing_id));

这些东西在许多字段中都是相关的,并且在每个字段中,每种语言都有 0-1 个 CanonicalName。直接的方法是

CREATE TABLE Fields(
    field_id INT PRIMARY_KEY,
    name VARCHAR[64])

CREATE TABLE CanonicalNames(
    thing_id INT
    field_id INT
    canonical_name_id INT
    FOREIGN KEY (thing_id) REFERENCES Things(thing_id),
    FOREIGN KEY (field_id) REFERENCES Fields(field_id),
    FOREIGN KEY (canonical_name_id) REFERENCES ThingNames(thing_name_id));

但这错过了 0-1 约束,这将是 field_id 以及 ThingNames 的 thing_idlanguage 列的唯一性约束由 canonical_name_id 引用。在 CanonicalNames 中包含所有列作为外键当然是多余且容易出错的,那么有没有办法在表之间施加唯一性约束?或者这里有我没有看到的更好的解决方案吗?

【问题讨论】:

  • 你的意思是 UNIQUE(thing_id,name) 在 ThingNames 中吗?
  • 不完全。由于事物的每种语言可能有 0-* 个名称,但每个字段和语言只有 0-1 个规范名称,因此我不能在 ThingNames 中施加唯一性约束。
  • 样本数据确实有助于解释这些关系。数据库标签也很有用。

标签: sql foreign-keys unique-constraint


【解决方案1】:

我不确定您的设计中有几件事。将 ThingNames.name 声明为键意味着同一事物不能在两种不同的语言中具有相同的名称,但这似乎可能发生在相关语言(例如挪威语和丹麦语)中,或者在未翻译技术术语时发生。

并且相关性的概念并没有在您的架构中明确表示。仅当事物具有至少一个规范名称(对于某些语言)时,它才与字段相关吗?

但是,做一些假设,我建议使用这个模型(基于 Dataphor 的伪代码,省略数据类型):

create table Thing {
  ThingId,
  ThingProperty,
  key { ThingID }
};

create table Field {
  FieldId,
  FieldName,
  key { FieldId },
  key { FieldName } // Assumption - or can several Fields have the same name?
};

create table Relevance { // Standard many-to-many association table
  ThingId,
  FieldId,
  key { ThingId, FieldId },
  reference Relevance_Thing { ThingId } references Thing { ThingId },
  reference Relevance_Field { FieldId } references Field { FieldId }
};

create table ThingName {
  ThingName,
  Language,
  ThingId,
  key { ThingName, Language }, // Assuming the same thing may have the same name in different languages
  reference ThingName_Thing { ThingId } references Thing { ThingId }
};

create table CanonicalName {
  ThingId,
  FieldId,
  Language,
  CanonicalName,
  key { ThingId, FieldId, Language },
  reference CanonicalName_Relevance { ThingId, FieldId } references Relevance { ThingId, FieldId },
  reference CanonicalName_ThingName { ThingId, Language, CanonicalName } references ThingName { ThingId, Language, ThingName }
};

CanonicalName 不在 BCNF 中,因为 FD { Canonicalname, Language } -> { ThingId },但冗余由引用 CanonicalName_ThingName 控制。 (您可以将其称为外键,但它实际上是外键。)这不是错误,它是您确保规范名称是事物名称之一的方式。 (我假设这是一条规则。)在这个设计中,在 CanonicalName 中有一个 Language 列并不是多余的,它会启用您缺少的 0-1 约束。

这种设计允许多个Thing在不同语言中具有相同的名称,同时也允许不同的Thing在不同语言中具有相同的名称。例如,“kjole”在挪威语和丹麦语中都表示“dress”,但挪威语中的“dress”在英语中表示“suit”。让我知道这是否应该被禁止,我会更新设计。

如果有一条规则表明事物与字段相关,当且仅当它具有该字段的至少一个规范名称时,可以(或可能应该)省略相关性表。当然,CanonicalName 必须引用 Field 而不是 Relevance。

【讨论】:

  • 相关性以与此问题正交的另一种方式处理,但您是对的,对事物名称的唯一性约束过于严格。除此之外,您的建议本质上是我的前提是错误的,并且受控冗余(我坚持认为它是冗余,因为它没有告诉我任何我从 canonical_name_id 不知道的东西)是可以接受的,甚至可能是必要的。我准备接受这个作为答案,但如果其他人有不同的看法,我会全力以赴。
  • 是的,CanonicalName.ThingId 是多余的,正如我(可能是间接地)声明的那样,但它是确保每个事物、字段和语言最多有一个规范名称的关键约束所必需的,以及确保规范名称是事物名称之一的外部超键。无冗余模式必须使用更复杂的约束来强制执行这些规则,而 SQL DBMS 通常不支持这些约束。这是一种权衡,但由外部超级键控制的冗余通常是最实用的解决方案。
猜你喜欢
  • 1970-01-01
  • 2021-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-11
  • 1970-01-01
  • 2021-09-30
相关资源
最近更新 更多