首页 / MySQL 入门教程 / 创建数据表

MySQL 入门教程

创建数据表

本教程共 46 篇 · 第 12 篇 · 更新于 2026-07-30 · 约 14 分钟阅读

MySQLMySQL 入门教程CREATE TABLEDDL建表NOT NULLDEFAULT表结构

12. 创建数据表

本节目标:学会用 CREATE TABLE 建表,掌握列定义、数据类型、NOT NULL/DEFAULT 约束的写法,理解表选项 ENGINE 和 CHARSET 的作用,能查看和验证表结构。

前面铺垫了那么多,这一节终于开始建表了。表是数据库里存数据的基本单位,建表就是给数据”设计格子”:有几列、每列存什么、有什么规矩。

12.1 CREATE TABLE 的基本语法

建表语句的骨架长这样:

CREATE TABLE [IF NOT EXISTS] 表名 (
    列名1 数据类型 [列约束],
    列名2 数据类型 [列约束],
    ...
    [表级约束]
) [表选项];

拆开看:

  • CREATE TABLE:建表关键字;
  • IF NOT EXISTS:可选,表已存在时不报错;
  • 表名:你要建的表叫啥;
  • 括号里:每一列的定义,列之间用逗号隔开;
  • 表选项:存储引擎、字符集等整张表的设置。

12.2 建第一张表:users

先建我们后面要反复用的 users 用户表:

USE shop;

CREATE TABLE users (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL,
    age TINYINT UNSIGNED,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

逐列解释:

  • id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY:无符号整数、自增、主键;
  • username VARCHAR(50) NOT NULL:变长字符串,最长 50,不能为空;
  • email VARCHAR(100) NOT NULL:变长字符串,最长 100,不能为空;
  • age TINYINT UNSIGNED:无符号小整数(0~255),可为空;
  • created_at DATETIME DEFAULT CURRENT_TIMESTAMP:日期时间,默认当前时间。

建成功返回:

Query OK, 0 rows affected (0.03 sec)
Note

建表前一定要先 USE 库名 选好库,否则会报 “No database selected”。也可以用全限定名 shop.users,但日常用 USE 更省事。

12.3 列定义的三要素

每一列的定义由三部分组成:

列名  数据类型  [约束]
  1. 列名:见名知意,用小写加下划线;
  2. 数据类型:前面几章学的,INTVARCHARDATETIME 等;
  3. 约束:可选,规定这列的规矩,如 NOT NULLDEFAULTPRIMARY KEY
-- 标准写法
username VARCHAR(50) NOT NULL

-- 也能这样写(类型和约束顺序可换,但不推荐)
username VARCHAR(50) NOT NULL DEFAULT ''

12.4 NOT NULL 约束

NOT NULL 表示这列不能存 NULL(空值),必须给值。

CREATE TABLE t (
    a INT NOT NULL,   -- 必须给值
    b INT             -- 可以为 NULL
);

插入时不给 a 值会报错:

INSERT INTO t(b) VALUES(1);
-- ERROR: Field 'a' doesn't have a default value
Tip

实战经验:能加 NOT NULL 就加。NULL 会带来很多麻烦—比较、计数、索引都会出问题。只有”确实可能没有值”的列(如中间名、备注)才允许 NULL。养成”默认 NOT NULL”的习惯。

12.5 DEFAULT 默认值

DEFAULT 给列设个默认值。插入时不指定这列,就自动用默认值。

CREATE TABLE orders (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    user_id INT UNSIGNED NOT NULL,
    amount DECIMAL(10,2) NOT NULL,
    status ENUM('pending','paid','shipped','done') NOT NULL DEFAULT 'pending',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
  • status 默认 'pending':新订单默认待处理;
  • created_at 默认 CURRENT_TIMESTAMP:自动填创建时间。

插入时省略这些列:

INSERT INTO orders(user_id, amount) VALUES(1, 99.50);

mysql> SELECT * FROM orders;
+----+---------+--------+---------+---------------------+
| id | user_id | amount | status  | created_at          |
+----+---------+--------+---------+---------------------+
|  1 |       1 |  99.50 | pending | 2026-07-30 14:30:00 |
+----+---------+--------+---------+---------------------+

statuscreated_at 自动填了默认值。

Note

几个特殊默认值:

  • CURRENT_TIMESTAMP:当前时间,可用于 DATETIME/TIMESTAMP
  • NULL:默认为空(列允许 NULL 时);
  • 字符串默认值要用引号:DEFAULT 'pending'
  • 8.0.13 起,BLOB/TEXT 列也能设默认值了(之前不行)。

12.6 PRIMARY KEY 主键

主键(Primary Key) 是能唯一标识每一行的列(或列组合),整张表只能有一个主键。

-- 单列主键
CREATE TABLE users (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    ...
);

-- 也可以单独一行写
CREATE TABLE users (
    id INT UNSIGNED AUTO_INCREMENT,
    ...
    PRIMARY KEY (id)
);

主键的特点:

  • 唯一:每行的主键值不能重复;
  • 非空:主键列自动是 NOT NULL
  • 建索引:主键自动建索引,查询快。

复合主键

多个列组合成一个主键:

CREATE TABLE order_items (
    order_id INT UNSIGNED,
    product_id INT UNSIGNED,
    quantity INT NOT NULL,
    PRIMARY KEY (order_id, product_id)
);

(order_id, product_id) 组合唯一,单个列可以重复,但组合不能重复。

Tip

复合主键用得少,日常更常用单列自增主键。复合主键适合”关联表”这种没有自然单列主键的场景。

12.7 表选项:ENGINE 和 CHARSET

建表时括号后面的表选项,控制整张表的”配置”。

ENGINE:存储引擎

) ENGINE=InnoDB;

指定存储引擎,不写就用默认的 InnoDB。除非有特殊原因,一律用 InnoDB(支持事务、行锁、外键)。查看服务器支持哪些引擎用 SHOW ENGINES;

CHARSET / COLLATE:字符集和排序规则

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

不写就继承数据库的设置。显式写上更清晰,推荐每张表都明确字符集。

Warning

如果库里既有 utf8 表又有 utf8mb4 表,连接查询时可能报 “Illegal mix of collations” 错误。全库统一 utf8mb4 能避免这类问题。

其他表选项

CREATE TABLE t (
    ...
) ENGINE=InnoDB
  DEFAULT CHARSET=utf8mb4
  AUTO_INCREMENT=1000   -- 自增起始值
  COMMENT='用户表';      -- 表注释
  • AUTO_INCREMENT=1000:自增列从 1000 开始;
  • COMMENT='用户表':给表加注释,方便维护。

12.8 IF NOT EXISTS 防报错

表已存在时再建会报错:

mysql> CREATE TABLE users (id INT);
ERROR 1050 (42S01): Table 'users' already exists

IF NOT EXISTS,存在就跳过:

CREATE TABLE IF NOT EXISTS users (
    id INT
);
-- Query OK, 0 rows affected, 1 warning (0.00 sec)
Note

IF NOT EXISTS 只保证”表存在就不建”,不检查表结构是否和你写的一致。已存在的表不会被修改。所以它主要用于脚本防报错,不能用来”幂等更新表结构”。

12.9 查看表是否建好

SHOW TABLES:列出所有表

SHOW TABLES;
+----------------+
| Tables_in_shop |
+----------------+
| orders         |
| users          |
+----------------+

SHOW CREATE TABLE:看建表语句

SHOW CREATE TABLE users\G

\G 让输出竖排,更易读:

*************************** 1. row ***************************
       Table: users
Create Table: CREATE TABLE `users` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL,
  `email` varchar(100) NOT NULL,
  `age` tinyint unsigned DEFAULT NULL,
  `created_at` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

这个命令非常有用,能看到 MySQL 实际存的完整表结构(包括它自动补的默认值、字符集等)。

12.10 DESCRIBE:看表结构概览

DESCRIBE(简写 DESC)用表格形式显示列信息,比 SHOW CREATE TABLE 更简洁:

DESC users;
+------------+------------------+------+-----+-------------------+----------------+
| Field      | Type             | Null | Key | Default           | Extra          |
+------------+------------------+------+-----+-------------------+----------------+
| id         | int unsigned     | NO   | PRI | NULL              | auto_increment |
| username   | varchar(50)      | NO   |     | NULL              |                |
| email      | varchar(100)     | NO   |     | NULL              |                |
| age        | tinyint unsigned | YES  |     | NULL              |                |
| created_at | datetime         | YES  |     | CURRENT_TIMESTAMP |                |
+------------+------------------+------+-----+-------------------+----------------+

各列含义:

  • Field:列名;
  • Type:数据类型;
  • Null:是否允许 NULL(NO 不允许);
  • Key:索引类型(PRI 主键);
  • Default:默认值;
  • Extra:额外信息(如 auto_increment)。
Tip

忘了表有哪些列、什么类型,第一反应就是 DESC 表名。这是日常用得最多的命令之一。

12.11 一个完整的建表示例

把这一节学的都用上,建两张配套的表(注意外键后面约束章细讲,这里先看结构):

USE shop;

-- 用户表
CREATE TABLE IF NOT EXISTS users (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL,
    age TINYINT UNSIGNED,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

-- 订单表
CREATE TABLE IF NOT EXISTS orders (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    user_id INT UNSIGNED NOT NULL,
    amount DECIMAL(10,2) NOT NULL,
    status ENUM('pending','paid','shipped','done') NOT NULL DEFAULT 'pending',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_user (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
  • IF NOT EXISTS 防报错;
  • 主键统一 INT UNSIGNED AUTO_INCREMENT
  • NOT NULL 的都加上;
  • created_at 用默认值自动填;
  • 显式指定 ENGINE=InnoDBCHARSET=utf8mb4
  • 加了表注释方便维护;
  • 订单表给 user_id 建了普通索引(INDEX 后面索引章讲)。

12.12 建表的设计心得

最后分享几条实战心得:

  1. 表名用名词复数或单数要统一:要么全 users/orders,要么全 user/order,别混;
  2. 主键统一叫 id:别有的表叫 user_id、有的叫 uid,乱;
  3. 每张表都带 created_at:记录创建时间,排查问题救命用;
  4. 能用 NOT NULL 就 NOT NULL:少用 NULL,省心;
  5. 金额用 DECIMAL,时间用 DATETIME,文本用 VARCHAR:类型选对,后面少返工;
  6. 建表前想清楚再加列:大表加列有代价,设计阶段多花十分钟想清楚;
  7. 字符集统一 utf8mb4:别留 latin1、utf8 的历史包袱。

12.13 小结

这一节学会了建表:

  • CREATE TABLE 基本语法:列定义 + 表选项;
  • 列定义三要素:列名、数据类型、约束;
  • NOT NULL 禁止空值,DEFAULT 设默认值,能加就加;
  • PRIMARY KEY 主键唯一标识行,自动非空、自动建索引;
  • 表选项 ENGINE=InnoDBCHARSET=utf8mb4
  • IF NOT EXISTS 防止表已存在报错;
  • SHOW TABLES 列表,SHOW CREATE TABLE 看建表语句,DESC 看结构概览。

表建好了,但需求会变,下一节讲怎么用 ALTER TABLE 修改表结构。