← 返回 MYSQL 列表

数据表设计

数据表设计

一、数据库三大范式

1.1 第一范式(1NF)

要求:每个字段都是不可分割的原子值。

反例

sql
-- 违反1NF:address 包含多个值
CREATE TABLE student_bad (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    address VARCHAR(200)  -- 存储 "北京市海淀区中关村大街1号"
);

正例

sql
-- 符合1NF:拆分到最小粒度
CREATE TABLE student_good (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    province VARCHAR(20),
    city VARCHAR(20),
    district VARCHAR(20),
    detail_address VARCHAR(100)
);

1.2 第二范式(2NF)

要求:满足1NF,且每个非主键列完全依赖于全部主键(消除部分依赖)。

反例

sql
-- 违反2NF:主键是 (order_id, product_id),但 product_name 只依赖 product_id
CREATE TABLE order_detail_bad (
    order_id INT,
    product_id INT,
    product_name VARCHAR(100),  -- 部分依赖:只依赖 product_id
    quantity INT,
    price DECIMAL(10,2),
    PRIMARY KEY (order_id, product_id)
);

正例:拆成两张表

sql
CREATE TABLE products (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    price DECIMAL(10,2)
);

CREATE TABLE order_items (
    order_id INT,
    product_id INT,
    quantity INT,
    PRIMARY KEY (order_id, product_id),
    FOREIGN KEY (product_id) REFERENCES products(id)
);

1.3 第三范式(3NF)

要求:满足2NF,且非主键列不传递依赖于主键。

反例

sql
-- 违反3NF:dept_name 传递依赖于 dept_id,而 dept_id 依赖于主键 id
CREATE TABLE employee_bad (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    dept_id INT,
    dept_name VARCHAR(50),  -- 传递依赖
    dept_location VARCHAR(100) -- 传递依赖
);

正例:拆成员工表和部门表

sql
CREATE TABLE department (
    id INT PRIMARY KEY,
    dept_name VARCHAR(50),
    location VARCHAR(100)
);

-- employee 中只需保留 dept_id
CREATE TABLE employee (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    dept_id INT,
    FOREIGN KEY (dept_id) REFERENCES department(id)
);

1.4 反范式设计

何时反范式:查询性能是瓶颈,且通过冗余字段能减少 JOIN 时。

sql
-- 反范式例子:在 orders 表中冗余 user_name
-- 虽然违反3NF,但避免了频繁 JOIN users 表
CREATE TABLE orders (
    id INT PRIMARY KEY,
    user_id INT,
    user_name VARCHAR(50),   -- 冗余字段
    amount DECIMAL(12,2),
    order_date DATETIME,
    -- 通过定时任务或业务逻辑保证一致性
    INDEX idx_user_id (user_id)
);

反范式原则

  • 冗余可控:冗余字段变更频率低(如用户名不常改)
  • 一致性保证:通过业务代码或触发器同步冗余字段
  • 收益大于成本:显著减少高频查询的JOIN

二、约束

2.1 主键约束(PRIMARY KEY)

sql
-- 单字段主键
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50)
);

-- 联合主键
CREATE TABLE course_score (
    student_id INT,
    course_id INT,
    score DECIMAL(5,2),
    PRIMARY KEY (student_id, course_id)
);

2.2 唯一约束(UNIQUE)

sql
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    phone VARCHAR(20) UNIQUE,         -- 手机号唯一
    email VARCHAR(100),
    UNIQUE KEY uk_email (email)       -- 指定约束名
);

2.3 非空与默认值

sql
CREATE TABLE products (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL,           -- 不能为空
    status TINYINT NOT NULL DEFAULT 1,    -- 默认值 1(启用)
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

2.4 外键约束(FOREIGN KEY)

sql
CREATE TABLE orders (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL,
    amount DECIMAL(12,2),
    FOREIGN KEY (user_id) REFERENCES users(id)
        ON DELETE RESTRICT       -- 删除用户时阻止
        ON UPDATE CASCADE        -- 更新用户ID时级联
);

生产建议:互联网高并发场景下不使用外键,外键约束由应用层保证,避免数据库层面的锁开销和性能瓶颈。


三、字段选型与命名规范

3.1 字段类型选择原则

类型 适用场景 不推荐场景
INT/BIGINT 整数ID、数量、状态 金额(用DECIMAL)
VARCHAR 变长字符串 定长内容(用CHAR)
CHAR 固定长度(手机号、身份证) 长文本
DECIMAL 金额、精确小数 浮点运算(用DECIMAL避免精度丢失)
DATETIME 日期时间 时间戳(INT存储)不够直观
TINYINT 状态标记(0/1) 多值状态(用SMALLINT)

3.2 字段类型选型细节

sql
-- 整数类型
TINYINT      -- 1字节,范围 0~255 或 -128~127
SMALLINT     -- 2字节
MEDIUMINT    -- 3字节
INT          -- 4字节
BIGINT       -- 8字节

-- 无符号推荐
id INT UNSIGNED NOT NULL AUTO_INCREMENT  -- 正数场景用 UNSIGNED

-- 金额类型
price DECIMAL(10,2) NOT NULL   -- 10位有效数字,2位小数
-- 如果需要更大范围
amount DECIMAL(18,2) NOT NULL

-- 字符串
VARCHAR(50)   -- 姓名、短名称
VARCHAR(255)  -- 常用最大长度(超过255会影响索引前缀)
TEXT          -- 长文本(不设默认值,无法直接创建全文索引以外的索引)

3.3 字段命名规范

sql
-- 推荐规范
CREATE TABLE order_item (          -- 表名:小写+下划线,单数
    id            BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
    order_id      BIGINT UNSIGNED NOT NULL COMMENT '订单ID',
    product_id    BIGINT UNSIGNED NOT NULL COMMENT '商品ID',
    product_name  VARCHAR(200) NOT NULL DEFAULT '' COMMENT '商品名称(快照)',
    product_price DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '商品单价',
    quantity      INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '购买数量',
    total_amount  DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT '小计金额',
    create_time   DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_time   DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    is_deleted    TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '逻辑删除:0-未删,1-已删',
    PRIMARY KEY (id),
    INDEX idx_order_id (order_id),
    INDEX idx_product_id (product_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单明细表';

3.4 企业建表规范完整示例

sql
-- ============================================
-- 表名:user
-- 描述:系统用户表
-- 作者:张三
-- 创建时间:2024-01-01
-- ============================================
CREATE TABLE `user` (
    `id`            BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
    `username`      VARCHAR(50)     NOT NULL DEFAULT '' COMMENT '用户名',
    `password_hash` VARCHAR(255)    NOT NULL DEFAULT '' COMMENT '密码哈希',
    `phone`         VARCHAR(20)     NOT NULL DEFAULT '' COMMENT '手机号',
    `email`         VARCHAR(100)    NOT NULL DEFAULT '' COMMENT '邮箱',
    `avatar_url`    VARCHAR(500)    NOT NULL DEFAULT '' COMMENT '头像URL',
    `status`        TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '状态:1-启用,0-禁用',
    `last_login_ip` VARCHAR(45)     NOT NULL DEFAULT '' COMMENT '最后登录IP',
    `last_login_time` DATETIME      DEFAULT NULL COMMENT '最后登录时间',
    `create_time`   DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `update_time`   DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    `is_deleted`    TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_username` (`username`),
    UNIQUE KEY `uk_phone` (`phone`),
    KEY `idx_create_time` (`create_time`),
    KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';

四、实战案例:电商核心表设计

sql
-- 用户表
CREATE TABLE `user` (
    `id`        BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    `nickname`  VARCHAR(50)    NOT NULL DEFAULT '',
    `phone`     VARCHAR(20)    NOT NULL DEFAULT '',
    `gender`    TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '0-未知,1-男,2-女',
    `birthday`  DATE           DEFAULT NULL,
    `level`     TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '会员等级',
    `create_time` DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_phone` (`phone`),
    KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

-- 商品表(反范式:冗余分类名称减少JOIN)
CREATE TABLE `product` (
    `id`            BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    `category_id`   INT UNSIGNED   NOT NULL DEFAULT 0,
    `category_name` VARCHAR(50)    NOT NULL DEFAULT '' COMMENT '冗余分类名',
    `name`          VARCHAR(200)   NOT NULL DEFAULT '',
    `price`         DECIMAL(10,2)  NOT NULL DEFAULT 0.00,
    `stock`         INT UNSIGNED   NOT NULL DEFAULT 0 COMMENT '库存',
    `sales_count`   INT UNSIGNED   NOT NULL DEFAULT 0 COMMENT '销量',
    `status`        TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '1-上架,0-下架',
    `create_time`   DATETIME       NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_category_id` (`category_id`),
    KEY `idx_status_sales` (`status`, `sales_count`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';

-- 订单表
CREATE TABLE `order` (
    `id`            BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    `order_no`      VARCHAR(32)    NOT NULL DEFAULT '' COMMENT '订单号',
    `user_id`       BIGINT UNSIGNED NOT NULL,
    `total_amount`  DECIMAL(12,2)  NOT NULL DEFAULT 0.00,
    `pay_amount`    DECIMAL(12,2)  NOT NULL DEFAULT 0.00,
    `pay_status`    TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '0-待支付,1-已支付,2-已退款',
    `delivery_status` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '0-待发货,1-已发货,2-已签收',
    `pay_time`      DATETIME       DEFAULT NULL,
    `create_time`   DATETIME       NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_order_no` (`order_no`),
    KEY `idx_user_id` (`user_id`),
    KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';

-- 订单明细表
CREATE TABLE `order_item` (
    `id`            BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    `order_id`      BIGINT UNSIGNED NOT NULL,
    `product_id`    BIGINT UNSIGNED NOT NULL,
    `product_name`  VARCHAR(200)   NOT NULL DEFAULT '' COMMENT '冗余:商品快照',
    `product_price` DECIMAL(10,2)  NOT NULL DEFAULT 0.00 COMMENT '冗余:下单时价格',
    `quantity`      INT UNSIGNED   NOT NULL DEFAULT 0,
    `subtotal`      DECIMAL(12,2)  NOT NULL DEFAULT 0.00,
    PRIMARY KEY (`id`),
    KEY `idx_order_id` (`order_id`),
    KEY `idx_product_id` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单明细表';

总结

设计原则 要点
三大范式 1NF 字段原子、2NF 消除部分依赖、3NF 消除传递依赖
反范式设计 减少JOIN、冗余低频变更字段、保证一致性的前提下做
字段选型 能用INT不用VARCHAR、金额用DECIMAL、TEXT谨慎
命名规范 小写+下划线、每个字段加COMMENT、表名单数
企业规范 逻辑删除、create_time/update_time、utf8mb4、索引必要