如何在关系数据库中实现1:N关系,其中一个N是特殊的?

时间:2014-01-02 15:24:49

标签: sql-server entity-framework database-design entity-relationship

我必须在用于存储动物行的MVC应用程序中实现关键字(我们开发数据库优先)。每个关键字与动物线具有M:N关系。

问题是每个关键字都有一堆同义词和替代拼写。因此,如果动物系对免疫学家特别感兴趣,它可能会获得关键字T-Cells。但是当用户输入t-cellt cellt Zellelymphocyte时,利益相关者希望该行能够找到。

我创建了一个数据库模型,其中关键字的实际拼写是一个具有1:N关系的单独表。

enter image description here

但我必须以某种方式在那里获得“规范”拼写。因此,一个拼写应该是主要的拼写,当显示动物线时显示,等等。其他拼写仅用于搜索(并且当输入新的动物线时可能自动完成,但这是一些未来的版本)。

幸运的是,规范拼写是应用程序范围内的规范,用户无法选择在他们刚输入的动物行上显示哪个同义词。

我可以想到三种方法,但这三种方法都有一些缺点:

  1. 在KeywordSpelling表中包含isCanonicalSpelling位。很简单,但是如果我的代码有错误或有人写了一个快速脚本来对数据库做某事,我可能最终会出现多于或少于一个相同关键字的拼写被标记为规范的情况。
  2. 在关键字表格中加入canonicalSpelling字段。然后,规范拼写不会写入拼写表。仍然相对容易,但不知何故感觉不洁,没有拼写表中的拼写。交换规范和非规范拼写的代码更复杂。
  3. 在Keyword和KeywordSpelling表之间建立第二个1:1关系。从ERD的角度来看,它感觉是最好的解决方案,但我不确定如何使用我正在使用的工具来很好地执行它。我不知道Microsoft SQL服务器是否允许1:1的关系,即使它确实存在,我也不知道为了让Entity Framework很好地发挥作用,它可能需要大量的代码。
  4. 那么,你认为哪种解决方案最好?哪一个会让我在未来最头痛,为什么?我忽略的任何解决方案都有缺点吗?有没有更好的解决方案我没有想到?

    我在这里发布而不是故意在dba.stackexchange上发布。我知道第三种解决方案从ERD的角度来看是最好的,但是我想知道哪一个允许最简单的应用程序级代码,而不会有太多不一致的数据风险。

2 个答案:

答案 0 :(得分:1)

非常简单地说:同义词不属于(仅)列表中的。根据定义,项目列表几乎是相同类型的项目的集合。但是,在某些情况下,您有两种类型的同义词在功能上不同(在其他情况下功能相同)。

我不知道任何默认解决方案,但我可以提出很多选择:

  • 总是是1'主'同义词吗?从来没有更少,从未更多?我建议在Keyword项中添加一列(nvarchar),输入正确的拼写。如果用户随后使用同义词,您可以轻松访问SynonymEntity.ParentKeyword.Name之类的内容。 (如果它有助于同义词搜索算法,您仍然可以使用完全相同的单词添加下面的同义词条目。稍微多一些数据要保留,但是当匹配所有可能的值时,您可以轻松地遍历列表。< / EM>)

  • 是否可能没有设置正确的名称?还是多个? (例如美国/英国英语)在这些场景中,我会在同义词表中选择一个额外的列来放置一个布尔值(IsCorrectSpelling)。如果您有多个,则可以找到所选语言的正确语句(例如,用于定义文化设置的第二列)。或者您可以使用MultipleSynonyms.FirstorDefault(word => word.IsCorrectSpelling)

但是为了给你一个更一般的答案,你必须这样看:主要同义词和非主要同义词不同(仅部分)。当您尝试为用户输入的内容找到匹配项时,它们是相同的。因此,就匹配算法而言,您可以将它们添加到集合中(即在这种情况下在同义词表中)。
但是当它归结为选择正确的拼写时,它们显然是不一样的,你需要一种方法来区分两种类型的同义词(即通过添加布尔值,或者通过在另一个位置注意正确的拼写,如Keyword表)。

因此,您通常希望找到一种可以将它们视为两者的方法。大多数时候这意味着你必须引入某种形式的继承,数据库中的重复数据等。你需要额外的复杂性。

答案 1 :(得分:1)

我将选项3 与自然键结合使用:

CREATE TABLE Keyword (
    ID INT PRIMARY KEY,
    CanonicalSpelling VARCHAR(100)
);

CREATE TABLE KeywordSpelling (
    ID INT,
    Spelling VARCHAR(100),
    PRIMARY KEY (ID, Spelling)
);

ALTER TABLE Keyword
    ADD FOREIGN KEY (ID, CanonicalSpelling)
    REFERENCES KeywordSpelling (ID, Spelling);

INSERT INTO Keyword VALUES (1, NULL);

INSERT INTO KeywordSpelling VALUES (1, 'T-Cells');
INSERT INTO KeywordSpelling VALUES (1, 't-cell');
INSERT INTO KeywordSpelling VALUES (1, 't cell');
INSERT INTO KeywordSpelling VALUES (1, 't Zelle');
INSERT INTO KeywordSpelling VALUES (1, 'lymphocyte');

UPDATE Keyword SET CanonicalSpelling = 'T-Cells' WHERE ID = 1;

请注意,字段CanonicalSpellingKeywordSpelling表中已存在的值的副本。这与选项2不同,后者只有一个独立的值。

乍一看,复制可能看起来多余,但请记住,DBMS将始终保持免于&#34;悬挂&#34;由于FK。

  • 这样,您可以继续以通用的方式查询KeywordSpelling表,并确信它包含所有值,包括规范。
  • OTOH,如果您只需要规范价值,您甚至不需要访问KeywordSpelling

选项2 需要特殊处理规范值 - 您需要同时查询KeywordKeywordSpelling以获取所有值,而您不能依靠DBMS来强制跨表的唯一性。

选项1 需要{ID, IsCanonical}上的索引(在KeywordSpelling表中)。幸运的是,MS SQL Server支持filtered indexes,因此影响将远远小于所有行的索引。由于MS SQL Server无论如何都无法强制执行真正的1:1(见下文),这实际上是一个可行的解决方案,但选项3仍然允许获取规范值而无需访问拼写表。


上面的结构允许缺少规范值(CanonicalSpelling是NULL的)。换句话说,它是一个1:0..1的关系。

在支持延迟约束的DBMS上(MS SQL Server遗憾地没有),您可以声明性地强制执行真正的1:1,如下所示:

CREATE TABLE Keyword (
    ID INT PRIMARY KEY,
    CanonicalSpelling VARCHAR(100) NOT NULL
);

CREATE TABLE KeywordSpelling (
    ID INT,
    Spelling VARCHAR(100),
    PRIMARY KEY (ID, Spelling)
);

ALTER TABLE Keyword
    ADD FOREIGN KEY (ID, CanonicalSpelling)
    REFERENCES KeywordSpelling (ID, Spelling)
    DEFERRABLE INITIALLY DEFERRED;

INSERT INTO Keyword VALUES (1, 'T-Cells');

INSERT INTO KeywordSpelling VALUES (1, 'T-Cells');
INSERT INTO KeywordSpelling VALUES (1, 't-cell');
INSERT INTO KeywordSpelling VALUES (1, 't cell');
INSERT INTO KeywordSpelling VALUES (1, 't Zelle');
INSERT INTO KeywordSpelling VALUES (1, 'lymphocyte');