POSTGRESQL外键引用两个不同表的主键

Jas*_*Zhu 23 postgresql foreign-key-relationship

我有两张桌子书籍和有声读物,两者都有ISBN作为主键.我有一个写的表有一个isbn属性,它对Books和Audiobooks ISBN有一个外键约束.当我插入到writeby中时出现的问题是postgresql希望我插入书写的ISBN在书籍和有声读物中.对我来说,有一个由存储作者和他们编写的书籍/有声读物编写的表格是有意义的,但这并不能转换为postgresql中的表格.我正在考虑实施的替代解决方案是有两个新关系audiobook_writtenby和books_writtenby但我不确定这是一个不错的选择.你能不能让我知道如何实现我最初的想法,即通过引用两个不同的表来编写单个表,或者如何更好地设计我的数据库.如果您需要更多信息,请与我们联系.

Mik*_*ll' 46

在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)
);
Run Code Online (Sandbox Code Playgroud)

  • 你是真正的 MVP,这种方法有什么限制吗? (2认同)
  • @hazer_hazer:“使用语言的优点;避免缺点。” (*《编程风格的要素》*,第 2 版,Kernighan 和 Plauger,第 19 页)在我看来,PostgreSQL 中的枚举是一个糟糕的枚举。除了我上面给出的原因(还有更多原因)之外,您还可以认为它们不适合关系模型,关系模型要求所有数据库数据都以表中的行列表示。话虽如此,*您*可以对 PostgreSQL 做任何您想做的事情。但你不能让我相信枚举在这里是个好主意。 (2认同)

kgr*_*ttn 7

您可以使用表继承来获得两全其美.使用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触发器.


dbe*_*hur 6

RDBMS不支持多态外键约束.您想要做的是合理的,但是在制作ORM系统时,关系模型和对象关系阻抗不匹配的真正问题之一并不适合.关于Ward's WIki的讨论很好

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

  • 关系模型和SQL数据库实际上很好地处理了这种事情.问题不是关系或SQL; 问题是其中一个明显的约束是错误地实现的.(约束条件是书籍和有声读物的ISBN来自同一个域.) (6认同)