你如何处理关系数据库中的m..n关系?

时间:2008-10-08 16:47:02

标签: sql database database-design

让我们看一个例子 - 书籍。一本书可以有1个作者。作者可以拥有1..m的书籍。代表书籍所有作者的好方法是什么?

我提出了创建Books表和Authors表的想法。 Authors表具有作者姓名的主AuthorID密钥。 Books表具有主要书籍ID和有关书籍的元数据(标题,出版日期等)。但是,需要有一种方法将书籍与作者和作者联系起来。这就是问题所在。

让我们说鲍勃有三本书。然而,在一本书中,他将其写成Bob博士。另一位他写作鲍勃博士,第三位是罗伯特博士。我希望能够确定这样一个事实,即这些作者实际上是同一个人,但却以不同的名字存在。我也希望将鲍勃与另一位写不同书籍的鲍勃区分开来。

现在让我们在应用程序中添加另一个部分,一个跟踪有趣人物的Person表。让我们说Bob是一个有趣的人。我想不仅要说这三本书的作者都是鲍勃,而且这个有趣的鲍勃与作者鲍勃是鲍勃一样。

那么对于这种可能复杂的映射存在什么策略,同时确保书籍作者通过封面上的名称来识别?

10 个答案:

答案 0 :(得分:29)

添加另一个名为BookAuthors的表,其中包含BookID,AuthorID和NameUsed的列。 NameUsed的NULL值意味着将其从Author的表中拉出来。这称为交叉表。

答案 1 :(得分:7)

你需要三张桌子 -

  1. 作者
  2. BookAuthors
  3. 图书将包含图书ID,图书标题以及您需要收集的有关图书的所有其他信息。

    作者将包含作者ID以及其他信息,例如您需要收集的关于任何特定作者的名字,姓氏。

    BookAuthors是一个多对多连接,包含BookID,AuthorID和NameUsed。这将允许该书具有零或多个作者,对于作者具有零或多本书,以及关于要捕获的该关系的信息。例如,你也可以在BookAuthor表上有一个专栏,用于描述作者与该书的关系(“编辑者”,“前言词”)。

答案 2 :(得分:6)

我认为你几乎就在那里。您需要一个Books表,一个Authors表,然后是一个“authors_of_books”表,其中包含该书的主键,作者的主键以及一个“引用为”的文本字段,显示该特定作者在该书中的引用方式

答案 3 :(得分:3)

这听起来像是多对多的关系,而不是1对多的关系。我想你会想要在这两者之间使用一个表,它允许你在其中任何一方定义1对多。看看这个......

http://www.tekstenuitleg.net/en/articles/database_design_tutorial/8

答案 4 :(得分:1)

鉴于Bob博士和Robert博士以及博士博士都是同一个人,他们会链接到作者表中的同一行。

但是,我认为你需要的是作者链接的人员表。您还可以将有趣的人员表链接到它。这样作者鲍勃和作者罗伯特以及有趣的鲍勃链接到人鲍勃。希望这是有道理的。

答案 5 :(得分:1)

首先想到的是有一个链接表,也许叫做AuthorOf来将书籍与作者联系起来。

列可以是AuthorID,BookID,也可能是CreditAs,因此您可以区分Bob博士和Bob博士。 (以及斯蒂芬金和理查德巴赫曼等笔名)。

您仍然可以唯一地识别作者。

答案 6 :(得分:1)

看起来你真的想要创建一系列自定义连接表,用于将项目从一个实体关联到另一个实体。

我会从最细微的层面开始,并且说任何作者必须是一个人。我会简化这个过程。

创建一个包含人员信息和PersonId的人员表,将信息放在那里。

然后创建一个BookAuthors表,其中有3列BookId,PersonId,TitledName。这样你可以根据需要使用不同的名称,如果没有,你可以使用COALESE或类似的东西获得默认名称,如果TitledName为null。

只是一个想法..

答案 7 :(得分:1)

你所问的真的不是你如何处理1..n关系,而是n..n关系(对作者有效,有很多书,一本书可以有很多作者)。

处理此问题的经典方法是通过中间表,所以

作者表(authorID,authorDetails) 书桌(bookID,书籍详情) AuthorBook表(authorID,bookID)

如果您真的为改变作者姓名而烦恼,那么请使用1..n作者详细信息表,添加

AuthorDetails(authorID,itemID,authorDetails)

并从作者表中删除authorDetails

答案 8 :(得分:0)

对于1..n关系(作者有很多书,作者有很多别名):

  1. 将外键author_id放入指向作者的书籍中。
  2. 创建一个新表author_aliases,以保存别名信息。
  3. 在指向别名的Books中放置外键alias_id(如果作者详细信息是默认的,则为nullable)。
  4. 将author_id外键放入author_aliases。
  5. 如果您愿意,可以使用中间表来链接作者和书籍,但是使用1..n映射我不认为这是必要的。

    对于n..m关系(作者有很多书,书有很多作者):

    您必须使用中间联接表(author_id,alias_id,book_id)而不是book表中的外键。您需要将别名中的外键保留为作者(以便轻松查找作者别名而无需通过所有书籍)。

    你可以争辩说,就未来的可扩展性而言,这也是一种更好的开始,即使初始规范说某事是1..n关系。您会发现给出的规格(或问题)通常是不充分的,因此您需要以一般方式设计规格何时更改或澄清。

答案 9 :(得分:0)

postgresql中的一个可能的实现,只是为了它的乐趣:

create table books (
  book_id integer primary key,
  title varchar not null
);

create table aliases (
  alias_id integer primary key,
  alias varchar not null
);

create table books_aliases (
  book_id integer references books (book_id),
  alias_id integer references aliases (alias_id),
  primary key (book_id, alias_id)
);

create table authors (
  author_id integer primary key,
  author varchar not null,
  interesting boolean default false
);

create table aliases_authors (
  alias_id integer references aliases (alias_id),
  author_id integer references authors (author_id),
  primary key (alias_id, author_id)
);

create view books_aliases_authors as
  select * from books 
    natural join books_aliases
    natural join aliases
    natural join aliases_authors
    natural join authors;

可以使用“使用”代替自然联接:

create view books_aliases_authors as
  select *
    from books 
    join books_aliases using (book_id) 
    join aliases using (alias_id) 
    join aliases_authors using (alias_id) 
    join authors using (author_id);

或为mysql兼容性做复杂的事情(注意mysql也需要上面varchars的显式最大长度):

create view books_aliases_authors as
  select b.book_id, title, l.alias_id, alias, t.author_id, author, interesting
    from books b
    join books_aliases bl on bl.book_id = b.book_id
    join aliases l on bl.alias_id = l.alias_id
    join aliases_authors lt on lt.alias_id = l.alias_id
    join authors t on t.author_id = lt.author_id;

这个例子不使用“people”表,只是作者的“有趣”标志。请注意,如果您将“作者”重命名为“人物”, nothing 会发生变化(结构上)