6.2 数据库基础概念 转载

来源:https://github.com/datawhalechina/vibe-vibe

本节目标:彻底搞懂关系型数据库的核心概念——表、行、列、主键、外键、关系类型、约束机制。

表:比 Excel 强大一百倍的"工作表"

数据库里的表(Table),你可以先理解为一个"超级 Excel 工作表"。如果你用过 Excel,理解数据库表就有了一个很好的起点。

先看相似的地方:

Excel 概念 数据库概念 说明
工作表 Sheet 表 Table 一类数据一张表,比如 users 表存所有用户
一行数据 行 Row(也叫记录) 一个具体的用户、一笔具体的订单
表头列名 列 Column(也叫字段) 数据的属性,比如 phoneaddress

再看不同的地方——这些差异才是数据库真正的价值,也是 Excel 永远做不到的:

数据类型严格。 Excel 里一列可以混放文字和数字,你在"年龄"列写个"很大"也没人拦你。数据库不行——integer 类型的列只能放整数,你写个文字进去,数据库直接报错拒绝。这看起来"不方便",但恰恰是保证数据质量的关键。想象小红的外卖平台,如果"价格"列里混进了文字,结算时就会出错。数据库的严格类型从源头杜绝了这种问题。

约束机制。 数据库能设置规则:手机号不能重复(UNIQUE)、昵称不能为空(NOT NULL)、订单的用户 ID 必须指向一个真实存在的用户(FOREIGN KEY)。违反规则的数据写不进去,从源头杜绝脏数据。这些规则一旦设定,无论是谁、通过什么方式写入数据,都必须遵守——不像 Excel,任何人都能随意修改任何格子。

表间关联。 这是数据库最核心的能力,也是它叫"关系型"数据库的原因。多张表可以通过"外键"建立关系,然后用 SQL 的 JOIN 操作把关联数据一次性查出来。比如"查出小红的所有订单以及每个订单里的菜品",一条 SQL 搞定。在 Excel 里,你得在多个 Sheet 之间来回跳,手动对照 ID,效率极低。

并发安全。 100 个用户同时下单,数据库通过事务和锁机制保证每笔订单都正确写入,不会互相覆盖、不会丢数据。Excel 做不到这一点——两个人同时编辑同一个文件,轻则产生冲突,重则丢失修改。

下面这个交互组件,让你直观感受一张表的结构——点击列名可以查看每个字段的详细说明:

外键:表与表之间的线索

如果说主键是"我是谁",那外键就是"我和谁有关系"。

外键(Foreign Key) 是一张表里指向另一张表主键的列。它的作用是建立表与表之间的关联,同时保证关联的数据真实存在。

举个例子:小红的 orders 表里有一列 user_id,它的值必须是 users 表里某个真实存在的 id。这就是外键。通过这个 user_id,你可以从一笔订单追溯到下单的用户——"这笔订单是谁下的?user_id 是 42,去 users 表查 id=42 的那行,哦,是小红。"

CREATE TABLE orders (
  id          serial PRIMARY KEY,
  user_id     integer REFERENCES users(id),  -- 外键,指向 users.id
  amount      real    NOT NULL,
  status      text    DEFAULT '待支付'
);

外键带来三个好处:

保证数据一致性。 你不能给一个不存在的用户创建订单。如果 users 表里没有 id=999 的用户,往 orders 表插入 user_id=999 的订单会直接报错。这就是"引用完整性"——你引用的东西必须真实存在,不能指向一个不存在的用户。

建立关联。 通过 user_id,你可以从一笔订单追溯到下单的用户,也可以从一个用户查到他的所有订单。数据之间的关系变得清晰可查。

级联操作(连锁反应)。 删除一个用户时,他的所有订单怎么办?数据库可以自动删除他的所有订单(级联删除),或者阻止删除(如果还有关联订单就不让删)。这个行为可以在建表时配置。

⚠️ 外键列必须加索引

PostgreSQL 不会自动给外键列创建索引。如果 orders 表有 10 万行,查"某个用户的所有订单"时,没有索引的 user_id 会导致全表扫描——从第一行翻到最后一行,像在没有目录的书里逐页找内容——慢 100 倍以上。所以每个外键列都要手动加索引。这个知识点在 6.4 会详细展开。

你可以在上方组件的「关系图」tab 中,点击连线查看外键的具体关联方式。

数据完整性:为什么约束重要

没有约束的数据库就像没有规则的仓库——什么都能往里塞,迟早乱套。今天塞进去一个"年龄 -5"的用户,明天塞进去一个"价格为空"的商品,后天你的应用就会在各种奇怪的地方崩溃,而且你根本不知道是数据的问题。

约束(Constraint) 是数据库自动执行的规则。每次写入数据时,数据库会检查这些规则,不合规的数据直接拒绝,连存都不让存。这就像机场安检——不管你是谁、从哪来,行李都要过 X 光机,违禁品一律拦截。

约束 作用 生活类比
PRIMARY KEY(主键) 唯一标识每行 身份证号,全国唯一
FOREIGN KEY(外键) 确保引用的数据存在 快递单上的收件人必须是真人
NOT NULL(必填) 不允许为空 表单里的必填项,不填不让提交
UNIQUE(不可重复) 不允许重复 手机号不能重复注册
CHECK(条件检查) 自定义条件检查 年龄必须大于 0,评分必须在 1-5 之间
DEFAULT(默认值) 未填时自动填入默认值 订单状态默认"待支付"

在上方组件的「约束演示」tab 中,你可以看到每种约束的正确 vs 违规插入对比。

你可能会想:我在代码里做校验不就行了,为什么还要在数据库层面加约束?

三个原因:

代码可能有 bug,但数据库约束是最后一道防线。 你的校验逻辑写错了、漏了一个边界条件、某个 if 判断少了个等号——这些都可能让脏数据溜进去。数据库约束不会犯这种错误,它是铁面无私的守门员。

数据的入口不止一个。 你的应用有 API 接口、有后台管理面板、有数据迁移脚本、有定时任务……每个入口都可能写入数据。你不可能在每个入口都完美地实现一遍校验逻辑。但数据库约束只需要定义一次,所有入口都受保护。

脏数据的清理成本极高。 想象几万条订单的 user_id 指向了不存在的用户——你怎么修?删掉这些订单?那用户的钱怎么办?把 user_id 改成某个默认用户?那数据就失真了。预防永远比治疗便宜。

SQLite vs PostgreSQL

SQLite PostgreSQL
定位 文件型数据库,零配置 完整的数据库服务器
存储 单个 .db 文件 独立的数据库服务进程
并发 单写多读,适合小规模 高并发读写,适合生产环境
数据类型 宽松(实际上只有 5 种) 严格且丰富(50+ 种)
JSON 支持 基础 强大的 jsonb 类型
适用场景 本地开发、嵌入式、原型验证 生产环境、团队协作、高并发
本教程建议 学习阶段可用,快速上手 实际项目推荐,Supabase 免费提供
💡 选择建议

如果你用 Supabase 或 Neon 等云数据库服务,直接就是 PostgreSQL,不需要纠结。本地开发想快速验证,SQLite 也完全够用——Drizzle ORM 支持两者无缝切换。

💡 本节核心要点
  • = 一类数据的容器,行是记录,列是属性
  • 主键 = 每行的唯一 ID,用 serial 自增
  • 外键 = 表间关联的线索,保证引用完整性
  • 三种关系:一对多(最常见)、多对多(需中间表)、一对一(拆分大表)
  • 约束 = 数据库层面的规则守卫,是代码校验之外的最后防线
  • 数据类型 = 选对类型,存储高效、查询快速
最后编辑:Alex 2026-03-05 11:39:51