我无法找到建立表之间关系的有效方法。我希望有一个书籍,作者,出版商和注册并拥有书架的用户数据库(阅读,正在阅读,想要阅读(或计划阅读))。我希望用户能够选择他们阅读,想要阅读或正在阅读的书籍。
P.S。我知道数据库表关系中的PK和FK。
编辑:也许这是一种更好的方法:
然后我将使用“状态”=(阅读,植物阅读和当前阅读) - 请告诉我这是否良好和有效!
答案 0 :(得分:6)
您需要books
和authors
之间的N:M链接,因为一本书可能有多位作者,而且每位作者可能写过多本书。在RDBMS中,这意味着您需要一个written_by
表。
books
和publishers
之间的链接不同。任何给定的图书只能有一个出版商(除非在您的系统中,不同版本的图书被视为同一本书)。所以你需要的只是publisher_id
books
外键
最后,最重要的是,您正在关注读者/用户。他们与书籍的关系。当然,这也是N:M关系。我当然希望人们阅读不止一本书(我们都知道如果你只读过一本书会发生什么......)当然,一本书不止一本人阅读。这需要一个book_users
连接表。这里真正的问题是,如何设计它。有三种基本设计。
按关系类型分隔表格。 (由@just_somebody概述)优点:你只有INSERTS和DELETES,永远不会更新。虽然这看起来很简洁,但在查询优化方面有所帮助,但大部分时间除了展示大型数据库图表外,它没有任何实际用途。
一个status
指标的表格。 (由@Hardcoded概述)优点:您只有一个表。缺点:您将拥有INSERTS,UPDATES和DELETES - RDBMS可以轻松处理的内容,但由于各种原因(后面会有更多内容)存在缺陷。此外,单个status
字段意味着一个读者只能拥有一个任何时候与书籍的连接,意味着他在任何时间点都只能处于plan_to_read
,is_reading
或has_read
状态,并且它会假定发生这种情况的时间顺序。如果那个人打算再次读取,或暂停,然后从开头等重读,这么简单的一系列状态指示器很容易就会失败,因为突然间那个人{{1}现在,还有is_reading
这件事。对于大多数应用程序而言,这仍然是一种合理的方法,并且通常有设计状态字段的方法,因此它们是互斥的。
日志。您将每个状态作为表中的新行插入 - 书籍和阅读器的相同组合将不止一次出现。您使用has_read
插入第一行和时间戳。另一个plan_to_read
。然后另一个is_reading
。优点:您只需要INSERT行,就可以得到完整事件的简洁年表。缺点:交叉表连接现在必须处理比上述更简单的方法更多的数据(并且更复杂)。
你可能会问自己,为什么要强调你在什么情况下插入,更新或删除?简而言之,每当您运行UPDATE或DELETE语句时,您很可能实际上丢失数据。那时你需要停止在你的设计过程中思考“我在这里失去了什么?”在这种情况下,您将失去事件的时间顺序。如果用户使用他们的书籍是您的应用程序的中心,您可能希望收集尽可能多的数据。即使现在无关紧要,这也是可能让您稍后进行“魔术”的数据类型。您可以了解某人阅读的速度,完成一本书所需的尝试次数等等。所有这些都无需向用户询问任何额外的输入。
所以,我的最终答案实际上是一个问题:
告诉某人去年读了多少本书会不会有帮助?
修改强>
由于可能不清楚日志的外观以及它的运行方式,以下是这样一个表的示例:
has_read
现在,无论何时书的状态发生变化,您都不会更新设计模式中的“user_read”表,而是在日志中插入相同的数据,现在这些数据填充了信息年表:
CREATE TABLE users_reading_log (
user_id INT,
book_id INT,
status ENUM('plans_to_read', 'is_reading', 'has_read'),
ts TIMESTAMP DEFAULT NOW()
)
当那个人真正开始阅读时,你会再做一次插入:
INSERT INTO users_reading_log SET
user_id=1,
book_id=1,
status='plans_to_read';
等等。现在你有一个“事件”数据库,由于时间戳列自动填充,你现在可以告诉发生了什么。请注意,此系统不能确保只存在一个特定用户帐簿对的“is_reading”。有人可能会停止阅读,然后继续阅读。您的加入必须考虑到这一点。
答案 1 :(得分:1)
首先为书籍,作者,出版商和作者创建4个表格。用户。比
答案 2 :(得分:1)
数据库表是数学relation,换句话说是predicate和一组该谓词为真的元组(“行”)。这意味着“表格”中的每个“行”都是(真)proposition。
这可能看起来都很吓人,但基本原则非常简单,值得严格掌握和理解:你会更好地了解你在做什么。
如果从小binary relation开始,关系很简单。例如,在所有整数的集合上有一个二元关系>
(大于),它“包含”所有有序的整数对x
,y
谓词x > y
成立的地方。注意:您不希望将此特定关系实现为数据库表。 :)
您希望图书,作者,出版商和用户拥有书架(阅读,正在阅读,想要阅读)。那个谓词是什么? “用户U已阅读书籍B”,“用户U正在阅读书籍B”,“用户U想要阅读书籍B”将是其中的一部分; “书B有ISBN#I,题目T,作者A”将是另一个,但有些书有多位作者。在这种情况下,你最好把它拆分成一个单独的谓词:“书B是由作者A写的。”
CREATE TABLE book (
id INT NOT NULL PRIMARY KEY
);
CREATE TABLE author (
id INT NOT NULL PRIMARY KEY
, name TEXT NOT NULL
);
CREATE TABLE written_by (
book INT NOT NULL REFERENCES book (id)
, author INT NOT NULL REFERENCES author (id)
);
CREATE TABLE reader (
id INT NOT NULL PRIMARY KEY
);
CREATE TABLE has_read (
reader INT NOT NULL REFERENCES reader (id)
, book INT NOT NULL REFERENCES book (id)
);
CREATE TABLE is_reading (
reader INT NOT NULL REFERENCES reader (id)
, book INT NOT NULL REFERENCES book (id)
);
CREATE TABLE plans_reading (
reader INT NOT NULL REFERENCES reader (id)
, book INT NOT NULL REFERENCES book (id)
);
等等。
答案 3 :(得分:1)
我会有一个图书表,其中包含:title,author,publisher,isbn。一个 Book_Statuses 表,包含一个id(PK)和一个状态(Read,Reading等)。 user_books 的第三个表,其中有一个与Books表相关的fk_book_id,以及一个链接到Book_Statuses表的fk_status_id。
所有这些共同为您提供了一个易于访问的数据结构。
这是假设我理解你的问题。如果您想拥有作者,出版商和图书的表格。我需要澄清你的需求。
答案 4 :(得分:1)
如果我是你,我会使用类似以下的模式:
TABLE user
-- Stores user's basic info.
( user_id INTEGER PRIMARY KEY
, username VARCHAR(50) NOT NULL
, password VARCHAR(50) NOT NULL
, ...
, ...
, ...
);
TABLE author
-- Stores author's basic info
( author_id INTEGER PRIMARY KEY
, author_name VARCHAR(50)
, date_of_birth DATE
, ...
, ...
, ...
);
TABLE publisher
-- Stores publisher's basic info
( publisher_id INTEGER PRIMARY KEY
, publisher_name VARCHAR(50)
, ...
, ...
, ...
);
TABLE book
-- Stores book info
( book_id INTEGER PRIMARY KEY
, title VARCHAR(50) NOT NULL
, author_id INTEGER NOT NULL
, publisher_id INTEGER NOT NULL
, published_dt DATE
, ...
, ...
, ...
, FOREIGN KEY (author_id) REFERENCES author(author_id)
, FOREIGN KEY (publisher_id) REFERENCES publisher(publisher_id)
);
TABLE common_lookup
-- This column stores common values that are used in various select lists.
-- The first three values are going to be
-- a - Read
-- b - Currently reading
-- c - Want to read
( element_id INTEGER PRIMARY KEY
, element_value VARCHAR(2000) NOT NULL
);
TABLE user_books
-- This table contains which user has read / is reading / want to read which book
-- There is a many-to-many relationship between users and books.
-- One user may read many books and one single book can be read by many users.
-- Hence we use this table to maintain that information.
( user_id INTEGER NOT NULL
, book_id INTEGER NOT NULL
, status_id INTEGER NOT NULL
, ...
, ...
, ...
, FOREIGN KEY (user_id) REFERENCES user(user_id)
, FOREIGN KEY (book_id) REFERENCES book(book_id)
, FOREIGN KEY (status_id) REFERENCES common_lookup(element_id)
);
TABLE audit_entry_log
-- This is an audit entry log table where you can track changes and log them here.
( audit_entry_log_id INTEGER PRIMARY KEY
, audit_entry_type VARCHAR(10) NOT NULL
-- Stores the entry type or DML event - INSERT, UPDATE or DELETE.
, table_name VARCHAR(30)
-- Stores the name of the table which got changed
, column_name VARCHAR(30)
-- Stores the name of the column which was changed
, primary_key INTEGER
-- Stores the PK column value of the row which was changed.
-- This is to uniquely identify the row which has been changed.
, ts TIMESTAMP
-- Timestamp when the change was made.
, old_number NUMBER(36, 2)
-- If the changed field was a number, the old value should be stored here.
-- If it's an INSERT event, this would be null.
, new_number NUMBER(36,2)
-- If the changed field was a number, the new value in it should be stored here.
-- If it's a DELETE statement, this would be null.
, old_text VARCHAR(2000)
-- Similar to old_number but for a text/varchar field.
, new_text VARCHAR(2000)
-- Similar to new_number but for a text/varchar field.
, old_date VARCHAR(2000)
-- Similar to old_date but for a date field.
, new_date VARCHAR(2000)
-- Similar to new_number but for a date field.
, ...
, ... -- Any other data types you wish to include.
, ...
);
然后,我会在几个表上创建触发器,以跟踪更改并在audit_entry_log
表中输入数据。
答案 5 :(得分:0)
你的答案是最好的方法。例如,假设您有书籍和类别表,并且书籍可以适合多个类别。保持这些数据创建第三个表以保持书籍类别关系的最佳方法。否则你必须为每个类别创建列。
ID name comedy adventure etc
5 BookName yes no no
像这样。这是最糟糕的事情。相信我。你的解决方案是最好的方法。
并且不知道PK&数据库表关系中的FK。如果您使用它们,它将比手动完成它们更快更安全。