
本文全面梳理 MySQL 数据库表的创建(CREATE)、修改(ALTER)、删除(DROP)三大操作,涵盖 DDL 语句基础、常用字段类型选型、主外键与约束设计、规范化建模思路,并结合典型业务场景展示“用户表”“订单表”“订单明细表”完整建表与演进案例,帮助读者掌握表结构的日常维护与迭代技巧。
在关系型数据库中,表是存储数据的核心载体。日常开发中,表的结构往往需要根据业务需求演进:新表上线、字段变更、索引优化、表拆分与归档,以及在项目废弃时的表删除。因此,熟练掌握 DDL(Data Definition Language)语句,能够保障线上变更的平滑与安全。
最常见的建表语句,用于定义新表及其列、索引、约束等。
CREATE TABLE `user` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`username` VARCHAR(50) NOT NULL COMMENT '用户名,唯一',
`password` CHAR(60) NOT NULL COMMENT '加盐哈希后密码',
`email` VARCHAR(100) DEFAULT NULL COMMENT '用户邮箱',
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1=活跃,0=禁用',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_username` (`username`),
INDEX `idx_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';用于在已有表上新增、修改、删除列、约束和索引。
新增列
ALTER TABLE `user`
ADD COLUMN `mobile` VARCHAR(20) NULL AFTER `email`,
ADD INDEX `idx_mobile` (`mobile`);修改列
ALTER TABLE `user`
MODIFY COLUMN `username` VARCHAR(100) NOT NULL;重命名列或表
ALTER TABLE `user`
CHANGE COLUMN `status` `state` TINYINT NOT NULL DEFAULT 1 COMMENT '状态';
-- 或者:
RENAME TABLE `user` TO `app_user`;删除列或索引
ALTER TABLE `user`
DROP COLUMN `mobile`,
DROP INDEX `idx_mobile`;彻底删除表及其数据、索引、约束,需要谨慎操作,建议配合事务或备份。
DROP TABLE IF EXISTS `order_item`;在设计表结构时,合理选型能提高存储效率与查询性能。
类型 | 范围(有符号) | 存储空间 | 适用场景 |
|---|---|---|---|
TINYINT | -128 ~ 127 | 1 字节 | 状态、标志位 |
SMALLINT | -32768 ~ 32767 | 2 字节 | 人数、评分等 |
INT | -2^31 ~ 2^31-1 | 4 字节 | 主键、计数、金额(小) |
BIGINT | -2^63 ~ 2^63-1 | 8 字节 | 全局 ID、累计金额 |
DECIMAL(m,n) | 精确小数,m 位总长度,n 位小数 | 可变 | 金额、汇率 |
良好的约束设计可保证数据一致性与完整性。
PRIMARY KEY (`order_id`, `item_id`)UNIQUE KEY `uniq_email` (`email`)ALTER TABLE `order`
ADD CONSTRAINT `fk_order_user`
FOREIGN KEY (`user_id`)
REFERENCES `user`(`id`)
ON DELETE CASCADE
ON UPDATE RESTRICT;NOT NULL:防止空值
CHECK(MySQL 8.0.16+ 支持):字段值校验
`status` TINYINT NOT NULL
CHECK (`status` IN (0,1,2));通过范式化保证数据不冗余、易维护。但对高并发、分析型场景,可考虑适度反范式或分表分库。
CREATE TABLE `user` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`username` VARCHAR(50) NOT NULL,
`password` CHAR(60) NOT NULL,
`email` VARCHAR(100) NOT NULL,
`status` TINYINT NOT NULL DEFAULT 1,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_username` (`username`),
UNIQUE KEY `uniq_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;CREATE TABLE `order` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` BIGINT UNSIGNED NOT NULL,
`order_no` CHAR(32) NOT NULL COMMENT '唯一订单号',
`total_amount` DECIMAL(10,2) NOT NULL,
`status` ENUM('NEW','PAID','SHIPPED','CLOSED') NOT NULL DEFAULT 'NEW',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_order_no` (`order_no`),
INDEX `idx_user_id` (`user_id`),
CONSTRAINT `fk_order_user` FOREIGN KEY (`user_id`)
REFERENCES `user`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;CREATE TABLE `order_item` (
`order_id` BIGINT UNSIGNED NOT NULL,
`item_id` INT UNSIGNED NOT NULL,
`product_name` VARCHAR(200) NOT NULL,
`quantity` INT UNSIGNED NOT NULL,
`unit_price` DECIMAL(10,2) NOT NULL,
PRIMARY KEY (`order_id`,`item_id`),
CONSTRAINT `fk_item_order` FOREIGN KEY (`order_id`)
REFERENCES `order`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;新增支付时间
ALTER TABLE `order`
ADD COLUMN `paid_at` DATETIME NULL AFTER `status`;分表分库:当单表行数或写入 QPS 达到千万级,可基于 order_no % N 进行分表;
归档老数据:定期将“超过一年未变更”的订单归档至历史表,减少主表压力。
ALGORITHM=INPLACE)减少业务中断;本项目适用于后台管理系统、电商用户中心、SaaS 用户模块等场景,特别适合开发者进行实战演练与面试准备。
我们将构建一个基础版的用户管理系统,具备以下业务功能:
涉及的核心业务对象包括:用户、角色、权限、日志等。
users)CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL,
email VARCHAR(100),
status TINYINT DEFAULT 1 COMMENT '0:禁用, 1:启用',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);roles)CREATE TABLE roles (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL UNIQUE,
description VARCHAR(255)
);permissions)CREATE TABLE permissions (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL UNIQUE,
code VARCHAR(50) NOT NULL UNIQUE COMMENT '用于权限标识,如 user:view'
);user_role)CREATE TABLE user_role (
user_id INT,
role_id INT,
PRIMARY KEY (user_id, role_id),
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (role_id) REFERENCES roles(id)
);role_permission)CREATE TABLE role_permission (
role_id INT,
permission_id INT,
PRIMARY KEY (role_id, permission_id),
FOREIGN KEY (role_id) REFERENCES roles(id),
FOREIGN KEY (permission_id) REFERENCES permissions(id)
);login_logs)CREATE TABLE login_logs (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
ip_address VARCHAR(45),
login_time DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);INSERT INTO roles(name, description) VALUES ('admin', '系统管理员'), ('user', '普通用户');
INSERT INTO permissions(name, code) VALUES
('查看用户', 'user:view'),
('新增用户', 'user:create'),
('删除用户', 'user:delete');
-- 分配权限给角色
INSERT INTO role_permission(role_id, permission_id) VALUES
(1, 1), (1, 2), (1, 3), -- admin 拥有全部权限
(2, 1); -- user 仅能查看用户INSERT INTO users(username, password, email) VALUES
('alice', 'hashed_pwd1', 'alice@example.com'),
('bob', 'hashed_pwd2', 'bob@example.com');
-- 分配角色
INSERT INTO user_role(user_id, role_id) VALUES
(1, 1), -- alice 为管理员
(2, 2); -- bob 为普通用户SELECT u.id, u.username, r.name AS role
FROM users u
JOIN user_role ur ON u.id = ur.user_id
JOIN roles r ON ur.role_id = r.id
WHERE u.status = 1;SELECT p.name, p.code
FROM users u
JOIN user_role ur ON u.id = ur.user_id
JOIN role_permission rp ON ur.role_id = rp.role_id
JOIN permissions p ON rp.permission_id = p.id
WHERE u.username = 'alice';SELECT u.username, l.ip_address, l.login_time
FROM login_logs l
JOIN users u ON l.user_id = u.id
WHERE l.login_time >= NOW() - INTERVAL 7 DAY
ORDER BY l.login_time DESC;SELECT *
FROM users
WHERE username LIKE '%bob%'
ORDER BY created_at DESC
LIMIT 0, 10;在角色授权或用户注册等业务流程中,可以使用事务来确保数据完整性。
START TRANSACTION;
INSERT INTO users(username, password, email) VALUES('charlie', 'hashed_pwd3', 'charlie@example.com');
SET @uid = LAST_INSERT_ID();
INSERT INTO user_role(user_id, role_id) VALUES(@uid, 2); -- 默认赋普通角色
COMMIT;START TRANSACTION;
-- 假设某权限不存在导致失败
INSERT INTO role_permission(role_id, permission_id) VALUES(1, 999);
-- 失败时回滚
ROLLBACK;users.username:用于登录验证、搜索login_logs.user_id:日志查询user_role.user_id / role_permission.role_id:JOIN 优化CREATE INDEX idx_username ON users(username);
CREATE INDEX idx_user_log ON login_logs(user_id);EXPLAIN SELECT u.username, r.name FROM users u JOIN user_role ur ON u.id = ur.user_id JOIN roles r ON ur.role_id = r.id;可查看索引是否使用、JOIN 类型、Rows 扫描数量等。