3. 数据库设计基础:关系型数据库概念、ER图设计、药盒用户表(user)字段定义

好,咱们进入第三个核心环节——数据库设计。说实话,很多初学者觉得数据库设计就是建几张表,随便写几个字段就完事了。我以前也这么想,直到有一次在项目里,因为表结构设计不合理,导致后期查询慢得像蜗牛爬,还被运维大哥吐槽了一整天……从那以后,我每次动手前都会老老实实画ER图、想清楚字段类型。

这一章,咱们就围绕药盒系统的用户数据存储,把关系型数据库的基础概念、ER图怎么画、以及user表到底该怎么定义,一次性讲透。

3.1 关系型数据库到底是什么?

说白了,关系型数据库就是一张张表格的集合。你想想看,Excel表格是不是也有行和列?关系型数据库里的“表”就类似Excel的sheet,每一行是一条记录,每一列是一个字段。

我个人的理解是:关系型数据库的核心在于“关系”二字。什么意思呢?就是表与表之间可以通过某些字段关联起来。比如用户表里有个字段叫“药盒ID”,药盒表里也有这个ID,那这两张表就能通过这个ID建立联系。

常见的数据库有MySQL、PostgreSQL、SQLite等。咱们这个药盒系统,我建议用SQLite,因为它轻量、不需要单独安装服务,非常适合嵌入式或小规模应用。嗯,这里要注意:如果你以后做大型项目,还是得用MySQL或PostgreSQL。

关系型数据库的三大特点:

  • 结构化存储:数据以表格形式组织,每行每列都有明确含义
  • 支持SQL查询:用结构化查询语言操作数据,非常灵活
  • 保证数据一致性:通过事务、约束等机制,防止数据出错

3.2 ER图设计——先画图,再建表

我见过太多人上来就写SQL建表,结果建到一半发现字段不够用,或者表之间关系搞混了。我的习惯是:先画ER图,再写代码。ER图就是实体-关系图,它帮我们把现实世界中的对象和它们之间的关系可视化。

咱们药盒系统,核心实体有哪些?我列一下:

  • 用户(User):用药的人
  • 药盒(Box):物理药盒设备
  • 用药计划(MedicationPlan):什么时候吃什么药
  • 服药记录(MedicationLog):每次服药的实际记录

它们之间的关系是这样的:

  • 一个用户可以有多个药盒(一对多)
  • 一个药盒可以有多个用药计划(一对多)
  • 一个用药计划对应多条服药记录(一对多)

画成ER图,大概长这样(文字描述版):

[用户] 1 —— N [药盒] 1 —— N [用药计划] 1 —— N [服药记录]

你想想看,如果一开始没画这个图,直接建表,很容易漏掉“药盒”这个实体。我以前就犯过这个错,把药盒信息直接塞到用户表里,结果一个用户有多个药盒时,数据冗余得一塌糊涂。

小技巧:画ER图时,先找出所有名词(用户、药盒、计划、记录),再找出动词(拥有、包含、记录),关系就出来了。用纸笔或者draw.io都行,别纠结工具。

3.3 药盒用户表(user)字段定义

好,重点来了。咱们先聚焦用户表,这是整个系统的基石。我直接给出字段定义,然后逐个解释为什么这么设计。

字段名 类型 长度 约束 说明
id INTEGER - PRIMARY KEY, AUTOINCREMENT 用户唯一标识,自增主键
name VARCHAR 50 NOT NULL 用户姓名
age INTEGER - CHECK(age > 0 AND age < 150) 年龄,加约束防止非法数据
phone VARCHAR 20 UNIQUE, NOT NULL 手机号,唯一且必填
medication_plan TEXT - - 用药计划(JSON格式存储)

我来逐个说说设计思路:

  • id:每个表都应该有一个主键,这是数据库设计的铁律。我习惯用自增整数,简单高效。UUID虽然更安全,但查询性能稍差,小项目没必要。
  • name:VARCHAR(50)够用了,中国人名字最长也就四五个字,50个字符绰绰有余。记得加NOT NULL,因为不知道用户叫啥,这系统就没意义了。
  • age:用整数,加CHECK约束。我曾经接手过一个项目,年龄字段居然存了“-5”这种值,就是因为没加约束。嗯,这里要注意:年龄最好用整数,别用字符串,否则排序和计算都会出问题。
  • phone:手机号用VARCHAR,别用整数。为什么呢?因为手机号可能以0开头,如果用整数存储,前面的0就丢了。加UNIQUE约束,保证一个手机号只能注册一个用户。
  • medication_plan:这个字段比较特殊。用药计划可能包含多种药、多个时间段,结构比较复杂。我建议用TEXT类型,存储JSON字符串。这样既灵活,又不需要额外建关联表。当然,如果你追求严格的数据库范式,可以单独建一张plan表。但咱们这个系统规模不大,JSON方案更实用。

避坑指南:我曾经在一个项目里把phone字段设成了INTEGER,结果用户输入“010-12345678”时,数据库直接报错。后来花了半天时间改表结构……所以记住:凡是可能包含非数字字符的字段,一律用VARCHAR

3.4 建表SQL示例

光说不练假把式,咱们直接上SQL代码。这是创建user表的完整语句:

CREATE TABLE user (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name VARCHAR(50) NOT NULL,
    age INTEGER CHECK(age > 0 AND age < 150),
    phone VARCHAR(20) UNIQUE NOT NULL,
    medication_plan TEXT
);

这段代码在SQLite里可以直接运行。如果你用MySQL,把AUTOINCREMENT改成AUTO_INCREMENT就行。其他数据库也大同小异。

我个人建议:建表时就把约束写全,别想着“以后再加”。因为数据一旦多起来,加约束的成本会非常高。你想想看,如果表里已经有一万条数据,突然要加一个UNIQUE约束,结果发现有重复值……那场面,啧啧。

3.5 本章小结

这一章咱们干了三件事:

  1. 理解了关系型数据库的本质——就是一张张有关联的表格
  2. 画了药盒系统的ER图,理清了用户、药盒、计划、记录之间的关系
  3. 详细定义了user表的5个字段,每个字段的类型、约束、设计理由都讲清楚了

下一章,咱们会继续设计药盒表(box)和用药计划表(medication_plan),到时候你会看到表与表之间是怎么通过外键关联起来的。嗯,敬请期待。