【问题标题】:Many foreign keys in one column一列中有许多外键
【发布时间】:2018-08-05 19:28:32
【问题描述】:

我在 Postgres 中有一个书籍数据库,我想添加一个 authorId 列,但是当一本书有很多作者时,我不知道如何使它工作。

作者表

authorId    firstName    lastName
1           Terry        Pratchett
2           Neil         Gaiman

书桌

bookId    name             authorId
1         Good Omens       1, 2

如何让它发挥作用?我的第一个想法是将 authorId 存储为数组。

我想要的查询看起来像这样

SELECT firstName, lastName 
FROM author 
WHERE authorId IN 
    (SELECT authorId 
     FROM book 
     WHERE bookId=1);

它会返回这个

firstName    lastName
Terry        Pratchett
Neil         Gaiman

但我有一种感觉,它不会起作用,并且有更好的方法来做到这一点。

【问题讨论】:

  • 这是一个可怕的想法。您应该了解规范化
  • 我不认为这是一个可怕的想法,规范化在实践中并不总是最好的选择(增加冗余、更多要管理的表、更多连接、性能差……),这是为什么 Postgres 有数组这种数据类型在工业中被广泛使用
  • @pedram bashiri:你错了。规范化从不增加冗余,因为规范化的主要目的是减少冗余。并且它还增加了一致性(在这个例子中,它保证您只将书籍链接到现有的作者 ID)。最后但同样重要的是:它增加了速度。使用桥接表查找书籍的作者通常非常快(提供了适当的索引),而数组方法往往非常慢,因为在表中查找书籍 ID 的唯一方法是读取所有记录。跨度>
  • @pedram bashiri:关于冗余:是的。我看不到桥接表在哪里增加了任何冗余。它没有。至于规范化:数据库应该被规范化。但是,数组不一定违反规范化。当您将某些内容存储为字符串时,您不应该对 DBMS 中的子字符串感兴趣(那么您应该单独存储子字符串)。数组也是一样:当你存储一个数组时,你不应该对该数组的单个元素感兴趣......
  • @pedram bashiri: ...在这个例子中,我们对单个数组元素非常感兴趣,因为它们甚至是 ID。将 ID 存储在数组中并不是一个好主意,因为这会导致不一致。但是,如果您想存储备忘录或类似内容,则数组可以非常方便。至于行业:我在相当大的数据库(超过一千个表)上工作了超过 25 年,我看到这里和那里都违反了规范化。这通常是有代价的。

标签: sql postgresql select


【解决方案1】:

教科书的解决方案是增加一个“映射”表来映射书籍和作者。这样一个作者可以写几本书,而一本书可能有多个作者:

CREATE TABLE book_authors (
    author_id INT NOT NULL,
    book_id INT NOT NULL,
    PRIMARY KEY book_authors_pk (author_id, book_id),
    FOREIGN KEY author_fk (author_id) REFERENCES authors (author_id),
    FOREIGN KEY book_fk (book_id) REFERENCES books (book_id)
)

然后您可以使用join 来查询一本书的作者:

SELECT first_name, last_name
FROM   authors a
JOIN   book_authors ba on a.author_id = ba.author_id
WHERE  book_id = 1

或者,如果您没有图书的 id,则使用 books 进行另一个联接将允许您查询其详细信息:

SELECT first_name, last_name
FROM   authors a
JOIN   book_authors ba ON a.author_id = ba.author_id
JOIN   books b ON ba.book_id = b.book_id
WHERE  b.name = 'Good Omens'

【讨论】:

    【解决方案2】:

    m:n 关系(一个作者可以写多本书 + 一本书可以由多个作者写)最好用桥接表建模:

    • 作者(author_id、first_name、last_name)
    • 书(书名,书名)
    • book_author (book_id, author_id)

    查询第一本书的作者:

    select first_name, last_name
    from author
    where author_id in (select author_id from book_author where book_id = 1);
    

    【讨论】:

      【解决方案3】:

      是的,您使用 postgres 的事实为您提供了将 authorIds 存储为数组的绝佳选择。你可以像这样定义你的表:

      CREATE TABLE Book (
       bookId serial not null,
       name varchar,
       authorIds int[]
      );
      

      你的选择查询应该是

      SELECT firstName, lastName 
      FROM author 
      WHERE authorId IN 
       (SELECT unnest(authorIds) 
        FROM Book 
        WHERE bookId = 1);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-03-23
        • 1970-01-01
        • 2015-04-01
        • 1970-01-01
        • 2016-03-22
        • 2012-03-14
        • 2013-01-07
        相关资源
        最近更新 更多