Python 现代开发学习笔记

(十二)从记录列表到关系表

1. 把重复信息分到对应的表

现在希望查询“每本书读了多久”“有基础标签的书有哪些”。JSON 中可以保存嵌套列表,但每次查找都要重新读入并遍历。可以把不同记录放进关系表,再让数据库按条件取得需要的结果。

books:一本书一行 标题 完成状态
id=1 Python 0
id=2 NumPy 1
id=3 Pygame 0
reading_log:一次阅读一行 所属书籍 book_id 分钟数
id=1 1 20
id=2 1 35
id=3 2 45

books.id 是主键,标识一行书籍;reading_log.book_id 是外键,指向对应的书籍。一次阅读不再重复保存完整书名,改标题后也不用逐条修改历史日志。

书籍、阅读日志与标签关系逐条建立

一本书有多次阅读,是一对多关系。一本书有多个标签,同一个标签也可以出现在多本书上,项目用 book_tags(book_id, tag) 保存对应关系,并用两个字段组成联合主键,阻止同一本书重复加入相同标签。

2. 用表结构表达记录规则

SQL 是描述表结构与数据操作的语言。先看建表时写下的规则:

-- 一本书一个稳定编号,本例把标题作为不可重复的书目名称。
CREATE TABLE books (
    id INTEGER PRIMARY KEY,
    title TEXT NOT NULL UNIQUE,
    finished INTEGER NOT NULL DEFAULT 0 CHECK (finished IN (0, 1))
);

-- 一次阅读对应已有书籍,分钟数保存为非负整数。
CREATE TABLE reading_log (
    id INTEGER PRIMARY KEY,
    book_id INTEGER NOT NULL REFERENCES books(id) ON DELETE CASCADE,
    day TEXT NOT NULL,
    -- typeof 检查实际存储类型,再与非负判断一起执行。
    minutes INTEGER NOT NULL CHECK (typeof(minutes) = 'integer' AND minutes >= 0)
);

NOT NULL 表示这一列要有值;UNIQUE 表示不同记录的该值不重复;DEFAULT 在插入省略该列时给出初值;CHECK 检查写入值。ON DELETE CASCADE 表示删除书籍时同时删除它的日志。本例的标题唯一是这份小书目程序采用的识别规则,实际区分同名不同版本时,可以另加版本或书目编号。

SQLite 常用存储类型有 INTEGER、REAL、TEXT、BLOB 和 NULL。布尔状态在这里存成 0/1,日期按约定存 ISO 文本;Python 侧再恢复成相应对象。声明、约束与具体转换一起构成记录结构。

笔记正文与全部题解 · Ctrl / ⌘ K