Skip to content

第 2 章 数据表的创建 ​

从一个生活场景说起 ​

假设你要管理一个班级的学生信息,最自然的做法是画一张 Excel 表格:

学号姓名年龄
1张三20
2李四22

在动手填数据之前,你得先做两件事:

  1. 设计表头:有哪几列?每列填什么类型的内容?学号能不能重复?姓名能不能留空?
  2. 把表格真正打印出来:光在脑子里设计没用,得落到纸面上。

数据库建表是同样的过程:

  • "设计表头" = 在 Python 中用代码描述表结构(表名、列、类型、约束);
  • "打印出来" = 让 SQLAlchemy 拿着这个设计去 MySQL 里执行 CREATE TABLE。

本章就是学习这两步。先记住一个对应关系:

Excel 概念数据库概念
一张工作表一张表(Table)
表头的一列一个字段/列(Column)
填的一行数据一条记录(Record/Row)
列的填写规则(必填、唯一)约束(Constraint)

核心概念 ​

两种定义表结构的风格 ​

SQLAlchemy 提供两种风格定义表结构:

  • Core 风格:用 Table 对象直接描述表——贴近 SQL 本身,本章使用;
  • ORM 风格:用 Python 类(映射类)描述表——更面向对象,第 8 章起使用。

两种风格殊途同归,最终都是生成 CREATE TABLE 语句发给 MySQL。

三个关键角色 ​

  • MetaData(元数据):可以理解为"设计图纸的档案柜"。你定义的所有表都登记在同一个 MetaData 里,它知道整个项目有哪些表。
  • Table(表):一张表的设计图纸,包含表名和所有列。
  • Column(列):图纸上的一栏,描述列名、数据类型、约束。

术语解释 —— 元数据(MetaData):"描述数据的数据"。表结构(有几列、什么类型)就是对数据的描述,所以叫元数据。

示例代码(逐行讲解) ​

python
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String

engine = create_engine(
    "mysql+pymysql://root:123456@localhost:3306/demo_db?charset=utf8mb4",
    echo=True,
)
metadata = MetaData()          # ① 准备一个空档案柜

user_table = Table(
    "user",                    # ② 表名(数据库里的名字)
    metadata,                  # ③ 把这张表登记进档案柜
    Column("id", Integer, primary_key=True, autoincrement=True),
    Column("name", String(50), nullable=False),
    Column("age", Integer),
)

# ④ 根据档案柜里登记的所有图纸,在数据库中真正建表
metadata.create_all(engine)

三个列定义逐一拆解:

列定义含义
Column("id", Integer, primary_key=True, autoincrement=True)整数类型的 id 列,是主键,值由数据库自动递增分配
Column("name", String(50), nullable=False)最长 50 字符的字符串列,不允许为空
Column("age", Integer)整数列,允许为空(默认)

术语解释 —— 主键(Primary Key):一张表里每行数据的"身份证号",用于唯一标识一行记录。主键不能重复、不能为 NULL。autoincrement=True 表示让数据库自动编号(1、2、3……),对应 MySQL 的 AUTO_INCREMENT。

执行后 MySQL 中实际执行的 SQL 大致为:

sql
CREATE TABLE user (
    id INTEGER NOT NULL AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL,
    age INTEGER,
    PRIMARY KEY (id)
)

打开 echo=True,你就能在控制台亲眼看到这条 SQL。

关键知识点 ​

概念说明
MetaData表结构的"登记册",集中存放所有 Table 定义
Table描述一张表:表名 + 列集合
Column描述一列:类型、主键、是否为空、默认值
create_all()把登记册里的表在数据库中创建出来(已存在则跳过)

常用列类型速查:

SQLAlchemy 类型MySQL 实际类型适合存放
IntegerINTEGER整数:年龄、数量
String(n)VARCHAR(n)短文本:姓名、标题
TextTEXT长文本:文章正文
FloatFLOAT小数(精度要求不高)
Numeric(10, 2)DECIMAL(10,2)金额(精确小数)
BooleanTINYINT(1)是/否
DateTimeDATETIME日期时间

经验之谈:存金额永远不要用 Float!浮点数有精度误差(0.1 + 0.2 ≠ 0.3),要用 Numeric(对应 MySQL 的 DECIMAL)。

深入理解:create_all 到底做了什么? ​

metadata.create_all(engine) 的工作流程:

  1. 遍历档案柜里登记的所有 Table;
  2. 对每张表,先问 MySQL:"这张表已经存在了吗?"(执行 SHOW TABLES 类似的检查);
  3. 不存在 → 生成并执行 CREATE TABLE;
  4. 已存在 → 直接跳过,什么都不做。

所以重复运行建表脚本是安全的,不会报错,也不会清空你的数据。

进阶内容 ​

1. create_all 的局限——它"只增不改"。 如果你在 user_table 里加了一列再运行 create_all(),已存在的表不会被修改。数据库结构的变更需要专门的迁移工具 Alembic(SQLAlchemy 官方出品),它像 Git 管理代码版本一样管理表结构版本。第 17 章的最佳实践会再次提到它。

2. 常用列约束一览。

python
Column("email", String(100), unique=True)        # 唯一约束:不允许重复
Column("status", Integer, default=1)             # 默认值:不填时自动为 1
Column("name", String(50), index=True)           # 建索引:加速按该列查询
Column("remark", String(200), nullable=True)     # 允许为空(默认行为)

3. 查看已有表的结构。 可以用 MySQL 命令行执行 DESC user; 或 SHOW CREATE TABLE user;,对照检查 SQLAlchemy 生成的表是否符合预期。

注意事项 ​

  • create_all() 是"只增不改"的:如果表已存在,它不会帮你修改表结构。改结构要用 Alembic 迁移工具。
  • 常用列类型:Integer、String(n)、Text、Float、Boolean、DateTime。
  • MySQL 中字符串必须指定长度,即 String(50),不能写成裸 String。
  • 主键建议用 Integer, primary_key=True, autoincrement=True(对应 MySQL 的 AUTO_INCREMENT)。
  • MySQL 的表默认引擎通常是 InnoDB,支持事务和外键,无需额外指定。
  • 表名和列名建议用小写字母 + 下划线(如 user_name),避免使用 MySQL 保留字(如 order、desc)作为名字。

本章小结 ​

  • 建表两步走:代码里描述表结构 → create_all() 在数据库中真正建表;
  • 三个角色:MetaData(档案柜)、Table(图纸)、Column(一栏);
  • 主键是每行的"身份证号",autoincrement 让数据库自动编号;
  • create_all() 只增不改,改表结构要靠 Alembic。