Sql 外键约束,某些列值驻留在其他表中

Sql 外键约束,某些列值驻留在其他表中,sql,postgresql,Sql,Postgresql,在PostgreSQL中,当FK列的一部分位于另一个表中时,表达外键约束的正确/惯用方法是什么 我将使用一个示例来说明这一点(省略一些明显的PK和FK以使其简短)。我们希望对书籍、书籍中的主题、阅读事件(在其中阅读书籍)和阅读事件中讨论的主题(应该是书籍主题的子集)之间的以下关联进行建模: 就SQL而言,我们有一个用来存放书籍的表: CREATE TABLE BOOK (name VARCHAR); INSERT INTO BOOK(name) VALUES('game of thrones')

在PostgreSQL中,当FK列的一部分位于另一个表中时,表达外键约束的正确/惯用方法是什么

我将使用一个示例来说明这一点(省略一些明显的PK和FK以使其简短)。我们希望对书籍、书籍中的主题、阅读事件(在其中阅读书籍)和阅读事件中讨论的主题(应该是书籍主题的子集)之间的以下关联进行建模:

就SQL而言,我们有一个用来存放书籍的表:

CREATE TABLE BOOK (name VARCHAR);
INSERT INTO BOOK(name) VALUES('game of thrones');
然后是一张桌子,上面放着我们在每本书中找到的各种主题:

CREATE TABLE BOOK_THEME (bookName VARCHAR, themeName VARCHAR);
INSERT INTO BOOK_THEME(bookName, themeName) VALUES ('game of thrones', 'ambition'), ('game of thrones', 'power');
然后创建一个表来记录有关“读取事件”的信息。每个阅读事件中只阅读一本书:

CREATE TABLE READING_EVENT(i SERIAL, venue VARCHAR, bookRead VARCHAR);
ALTER TABLE READING_EVENT ADD PRIMARY KEY (i);
INSERT INTO READING_EVENT(venue, bookRead) VALUES('Municipal Library', 'game of thrones');
CREATE TABLE READING_EVENT_DISCUSSION(i INTEGER, themeDiscussed VARCHAR);
ALTER TABLE READING_EVENT_DISCUSSION ADD CONSTRAINT constr1 FOREIGN KEY (i) REFERENCES READING_EVENT(i);
这里是棘手的部分。我们还有一个表格记录了在阅读活动期间积极讨论的主题:

CREATE TABLE READING_EVENT(i SERIAL, venue VARCHAR, bookRead VARCHAR);
ALTER TABLE READING_EVENT ADD PRIMARY KEY (i);
INSERT INTO READING_EVENT(venue, bookRead) VALUES('Municipal Library', 'game of thrones');
CREATE TABLE READING_EVENT_DISCUSSION(i INTEGER, themeDiscussed VARCHAR);
ALTER TABLE READING_EVENT_DISCUSSION ADD CONSTRAINT constr1 FOREIGN KEY (i) REFERENCES READING_EVENT(i);

现在,我如何表达
主题讨论
专栏显然必须引用在该事件中阅读的书中实际找到的主题之一?
bookName
列出现在
READING\u EVENT
表中,而不是出现在我们希望声明FK的
READING\u EVENT\u讨论中。

您省略了书名上的所有外键

这就是为什么我用一套完整的增强的表定义来回答,这是关于外键的,对吗?舒尔你举了一个简单的例子

要解决的问题是,
reading\u event\u discussion
中的记录必须与该书中存在的主题有关:

drop table book cascade;
drop table book_theme;
drop table reading_event cascade;
drop table reading_event_discussion;

create table book (
    name text primary key -- new, a must because it is FK in reading_event
);
insert into book (name) values ('game of thrones'),('Database design');

create table book_theme (
    bookname  text references book(name), -- new
    themename text
);
insert into book_theme (bookname, themename) values 
  ('game of thrones', 'ambition'), ('game of thrones', 'power');

create table reading_event (
  i        SERIAL primary key, 
  venue    text, 
  bookread text references book(name) -- FK is new
);
insert into reading_event (venue, bookRead) VALUES
  ('Municipal Library', 'game of thrones');  

-- this is the solution: extended reference check
create or replace function themecheck (i integer, th text) returns boolean as $$
    select 
     (th in (select themename from book_theme bt 
       join reading_event re on i=re.i and re.bookRead=bt.bookname))
$$ language sql;

create table reading_event_discussion (
    i integer references reading_event(i), 
    themeDiscussed text check (themecheck (i, themeDiscussed))
);

-- Test statements:
-- just check data
select * from reading_event;
-- this should be ok
insert into reading_event_discussion values (1,'ambition'),(1,'power');
-- this must be refused
insert into reading_event_discussion values (1,'databases');
因此,解决方案是编写一个自定义检查函数。这是不可移植到其他数据库系统的


可以用几种语言(plpgsql、pltcl等)编写此函数,但SQL函数可以内联到查询中,可能会更快。

现在正好解决了同样的问题。考虑这个问题(已经包含了你问题的答案的一部分)和我的答案。我添加了一个示意图,它显示了基本的对应关系,实际上,它似乎是相同的模式。然而,有一个关键的区别:在这个问题中,
pin_inst
表确实是多余的(正如您在回答中指出的)。零件实例的接点实例始终是该零件的所有接点。在我的例子中,阅读活动中讨论的主题是书中主题的子集。所以我的理解是,只有通过触发器才能实现。我发现这个问题相当令人困惑。模式确实是一样的,但细节在这个问题上并不清楚(至少对我来说)。看到这一个和答案:转换
艺术家->书籍
专辑->阅读事件
曲目->书籍主题
专辑->阅读事件讨论
。唯一的区别是,在
AlbumTrack
中不需要
TrackNo
,主键应该是(这里提到的)
(trackID,albumID)->(ThemeName,i)
(在阅读事件中不允许出现两次主题)。@ypercube i stand correct。问题的关键是,在您提供的链接中,在相册表中,到艺术家表的FK也是相册主键的一部分,而在我的情况下,阅读的书籍不是阅读事件PK的一部分,在Erwin的链接中,部分id不是pin PK的一部分。但我同意,使用你提出的组织确实解决了问题。