首页/数据/SQL 速查/CREATE TABLE 建表要点
变更与事务52 条

CREATE TABLE 建表要点

建表时把主键、约束、字符集、引擎一次定清楚:MySQL 常用 InnoDB + utf8mb4;PostgreSQL 关注类型与约束表达。

SQL 速查数据更新于 2026-10-13

CREATE TABLE 是什么

CREATE TABLE 在建表时把主键、约束、字符集与存储引擎一次定清楚,因为表一旦有数据,改类型的代价远高于一次写对。MySQL 8.0 默认引擎就是 InnoDB,字符集建议显式写 utf8mb4,才能完整支持 Unicode 与 emoji;PostgreSQL 更关注类型选择与约束表达,自增列推荐写 GENERATED ALWAYS AS IDENTITY,而不是已不推荐的 serial。金额一律用 DECIMAL 或 numeric 定点类型,时间列给默认值,主键必建,常用查询条件提前规划索引,这几点在两库通用。

基本信息

适用平台MySQL 8.0 与 PostgreSQL 12 及以上
MySQL 引擎显式写 ENGINE=InnoDB,8.0 默认即为 InnoDB
MySQL 字符集显式写 DEFAULT CHARSET=utf8mb4,不要依赖服务器默认值
PostgreSQL 自增GENERATED ALWAYS AS IDENTITY,替代旧的 serial
金额类型DECIMAL(p,s) 或 numeric(p,s),不用 FLOAT 与 DOUBLE
时间列默认值MySQL 用 CURRENT_TIMESTAMP,PostgreSQL 用 now()
索引声明MySQL 可写在 CREATE TABLE 内,PostgreSQL 通常独立建索引

典型使用场景

  • 新业务建表:一次把主键、时间列与常用查询索引定好
  • 设计文档落地:把 ER 图转成 DDL,字段类型与约束逐条对齐
  • 在线变更:先建同构的影子表,迁完数据再改名切换
  • 归档表:按时间维度建结构一致的归档表
  • 测试环境初始化:脚本化建表,保证环境可重复搭建
  • 分表场景:按业务键建同构子表,字段与索引保持一致

MySQL 建表

MySQL 建表
CREATE TABLE orders (
  id         BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,   # 主键用 BIGINT 起步
  user_id    BIGINT UNSIGNED NOT NULL,                   # 关联用户
  amount     DECIMAL(12,2)   NOT NULL DEFAULT 0.00,      # 金额用定点类型
  status     TINYINT         NOT NULL DEFAULT 0,         # 状态用小整型
  created_at DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  KEY idx_user_created (user_id, created_at)             # 联合索引:等值列在前
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

PostgreSQL 建表

PostgreSQL 建表
CREATE TABLE orders (
  id         bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,   # 自增主键
  user_id    bigint        NOT NULL,
  amount     numeric(12,2) NOT NULL DEFAULT 0,                  # 定点类型
  status     smallint      NOT NULL DEFAULT 0,
  created_at timestamptz   NOT NULL DEFAULT now(),
  updated_at timestamptz   NOT NULL DEFAULT now()
);
CREATE INDEX idx_orders_user_created ON orders (user_id, created_at);

建表要点对照

要点MySQLPostgreSQL
自增主键AUTO_INCREMENTGENERATED ALWAYS AS IDENTITY
字符集DEFAULT CHARSET=utf8mb4由库编码决定,通常 UTF8
时间类型DATETIME / TIMESTAMPtimestamp / timestamptz
索引声明可写在 CREATE TABLE 内通常独立 CREATE INDEX
布尔类型TINYINT 约定 0 与 1boolean 原生支持
不写字符集就依赖服务器默认值,老实例可能是 latin1,新老库混用后中文与 emoji 直接出错;建表时显式声明最省心。
自增主键从 BIGINT 起步,避免业务增长后改类型;主键类型一旦上线要改,牵涉所有关联表,代价很高。
PostgreSQL 的 timestamptz 会做时区转换,timestamp 不会;用哪种要在建模阶段统一,混用会造成时间显示错乱。
给列加 NOT NULL 与默认值能挡掉一批脏数据,可空列越多,后续查询要处理的空值边界就越多。

建表实践要点

  • 主键必建:没有主键的表在复制与在线变更时问题很多
  • 字段尽量给默认值:新增列给默认值能避免应用侧到处判空
  • 类型一次选对:金额定点、状态小整型、时间带时区信息
  • 索引与查询一起设计:常用等值加范围条件组成联合索引
  • 表与字段写注释:COMMENT 能省下后来人大量猜测时间
  • 预留扩展:新增枚举值不要写进注释当契约,改用独立字典表

易错点

  • 不写字符集依赖服务器默认值:老实例默认可能是 latin1,新库与旧库混用后中文与 emoji 直接出错
  • 自增主键用 INT 起步,业务量增长后需要改类型,牵动所有关联表
  • 金额用 FLOAT 或 DOUBLE 存储,累加后出现精度偏差,对账对不上
  • 可空列过多且不给默认值,业务代码里到处判空,忽略一处就出空指针
  • 上线后才发现缺少必要的索引,只能在大表上补建,产生锁与性能抖动

数据来源与参考

  • MySQL 8.0 Reference Manual · CREATE TABLE
  • PostgreSQL Documentation · CREATE TABLE
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌