43

我有两个表 Books 和 Audiobooks,它们都以 ISBN 作为主键。我有一个 writeby 表,该表具有一个isbn对书籍和有声读物 ISBN 具有外键约束的属性。

当我插入时出现的问题writtenby是 postgresql 希望我插入的 ISBNwrittenby同时出现在书籍和有声读物中。

writtenby有一个表来存储作者和他们写的书籍/有声读物对我来说很有意义,但是这并不能转换为 postgresql 中的表。

我正在考虑实施的替代解决方案是建立两个新关系audiobook_writtenbybooks_writtenby但我不确定这是一个好的替代方案。

您能否告诉我如何实现我最初的想法,即让一个表writtenby引用两个不同的表,或者如何更好地设计我的数据库?如果您需要更多信息,请与我们联系。

4

4 回答 4

71

在 PostgreSQL 中有不止一种方法可以做到这一点。就个人而言,我更喜欢这种方式。

-- This table should contain all the columns common to both 
-- audio books and printed books.
create table books (
  isbn char(13) primary key,
  title varchar(100) not null,
  book_type char(1) not null default 'p'
    check(book_type in ('a', 'p')),
  -- This unique constraint lets the tables books_printed and books_audio 
  -- target the isbn *and* the type in a foreign key constraint.
  -- This prevents you from having an audio book in this table 
  -- linked to a printed book in another table.
  unique (isbn, book_type)
);

-- Columns unique to printed books.
create table books_printed (
  isbn char(13) primary key references books (isbn),
  -- Allows only one value. This plus the FK constraint below guarantee
  -- that this row will relate to a printed book row, not an audio book
  -- row, in the table books. The table "books_audio" is similar.
  book_type char(1) default 'p'
    check (book_type = 'p'),
  foreign key (isbn, book_type) references books (isbn, book_type),
  other_columns_for_printed_books char(1) default '?'
);

-- Columns unique to audio books.
create table books_audio (
  isbn char(13) primary key references books (isbn),
  book_type char(1) default 'a'
    check (book_type = 'a'),
  foreign key (isbn, book_type) references books (isbn, book_type),
  other_columns_for_audio_books char(1) default '?'
);

-- Authors are common to both audio and printed books, so the isbn here
-- references the table of books.
create table book_authors (
  isbn char(13) not null references books (isbn),
  author_id integer not null references authors (author_id), -- not shown
  primary key (isbn, author_id)
);
于 2012-04-09T18:29:24.833 回答
8

您可以使用表继承来获得两全其美的效果。INHERITS使用引用 writeby 表的子句创建 audiobook_writtenby 和 books_writtenby 。正如您所描述的,外键可以在子级别定义,但您仍然可以在更高级别引用数据。(您也可以使用视图执行此操作,但听起来在这种情况下继承可能更清晰。)

请参阅文档:

http://www.postgresql.org/docs/current/interactive/sql-createtable.html

http://www.postgresql.org/docs/current/interactive/tutorial-inheritance.html

http://www.postgresql.org/docs/current/interactive/ddl-inherit.html

请注意,如果您这样做,您可能希望在 writeby 表上添加一个 BEFORE INSERT 触发器。

于 2012-04-09T03:04:35.803 回答
5

RDBMS 不支持多态外键约束。您想要做的事情是合理的,但不是关系模型很好地适应的事情,也是制作 ORM 系统时对象关系阻抗不匹配的真正问题之一。在 Ward 的 WIki 上对此进行了很好的讨论

解决问题的一种方法可能是创建一个单独的表 known_isbns,并在书籍和有声书上设置约束和/或触发器,以便该表包含两种特定类型书籍表的所有有效 isbns。然后您对 writeby 的 FK 约束将检查 known_isbns。

于 2012-04-09T01:48:33.503 回答
-2

在这个特定示例中,绝对不需要使用多个表。只需使用表“Book”并添加“AudioBook”中的列(如果适用)。如果您必须在表级别上区分非常具体的列,请创建视图。您是否检查过内容相同的“书籍”和“有声读物”是否具有相同的 ISBN?

于 2012-05-10T13:19:16.597 回答