多个外键作为主键postgres,我应该这样做吗?

multiple foreign keys as primary key postgres, should I do it?

这是其中之一:为什么我应该或为什么不应该。

所以我的图书应用程序有评论,但一个用户不能对同一本书评论超过一次。在我看来,为评论创建 table 并将 user_id 和 book_id (ISBN) 作为评论 table 的主键是有意义的。但是,如果应用程序获得太多评论,在某些时候是否会成为问题,例如,该决定会减慢查询速度吗?

我正在使用 postgres,我不确定下面的代码是否正确:

CREATE TABLE users(
    user_id PRIMARY KEY SERIAL,
    user_name VARCHAR NOT NULL UNIQUE,
    pass_hash VARCHAR NOT NULL,
    email VARCHAR NOT NULL UNIQUE,
);

CREATE TABLE books(
    book_id PRIMARY KEY BIGINT,
    author VARCHAR NOT NULL,
    title VARCHAR NOT NULL,
    year INT NOT NULL CHECK (year > 1484),
    review_count INT DEFAULT 0 NOT NULL,
    avrg_score FLOAT,
);

CREATE TABLE reviews(
    user_id INT FOREIGN KEY REFERENCES users(user_id) NOT NULL
    book_id INT FOREIGN KEY REFERENCES books(book_id) NOT NULL
    score INT NOT NULL CHECK (score > 0, score < 11)
    PRIMARY KEY (book_id, user_id)

);

这是一个完全有效的设计选择。

您的图书和用户之间存在多对多关系,由 reviews table 表示。拥有基于两个外键的复合主键可以让您强制执行参照完整性(给定的元组只能出现一次),同时为 table.

提供主键

另一种选择是为桥table设置一个代理主键。如果您需要从另一个 table 引用 reviews,这会使事情变得更容易,但是您仍然需要对两个外键列进行唯一约束以确保完整性,因此这实际上会导致额外的 space 正在使用中。

关于您的代码,它有几个问题:

  • primary key 关键字位于数据类型

  • 之后
  • check 约束的格式不正确

  • 此处和此处缺少或添加逗号

考虑:

CREATE TABLE users(
    user_id SERIAL PRIMARY KEY ,
    user_name VARCHAR NOT NULL UNIQUE,
    pass_hash VARCHAR NOT NULL,
    email VARCHAR NOT NULL UNIQUE
);

CREATE TABLE books(
    book_id BIGINT PRIMARY KEY,
    author VARCHAR NOT NULL,
    title VARCHAR NOT NULL,
    year INT NOT NULL CHECK (year > 1484),
    review_count INT DEFAULT 0 NOT NULL,
    avrg_score FLOAT
);

CREATE TABLE reviews(
    user_id INT REFERENCES users(user_id) NOT NULL,
    book_id INT REFERENCES books(book_id) NOT NULL,
    score INT NOT NULL CHECK (score > 0 and score < 11),
    PRIMARY KEY (book_id, user_id)

);

我上面的内容很好,但我会从书中删除列 review_count 和 avrg_score。这些在需要时是可推导的。如果您的应用程序需要,那么不要存储它们,而是创建一个视图来派生它们。这避免了维护 运行 值的复杂过程:

create view books_vw as 
    select b.book_id  
         , b.author 
         , b.title 
         , b.year 
         , count(r.*) review_count 
         , avg(r.score) avrg_score
      from books   b 
      left join reviews r
        on r.book_id = b.book_id
     group by 
           b.book_id  
         , b.author 
         , b.title 
         , b.year                  
 ;