资讯动态

学校管理系统四张表DDL设计:从建模到完整SQL脚本

发布时间:2026/10/13 13:32:43 来源:尧图企业网站定制
刚接到这套需求的时候对方只丢了一句话SchoolDB 对应的 4 张表 DDL 语句仅有结构。听起来像是个不起眼的初始化脚本但真正动笔之前我反而觉得这是最值得认真对待的部分。SchoolDB 这类学校管理系统无论后面接的是选课、成绩、排课还是教师管理底层都逃不开这几张表结构定错了后面业务代码写得再漂亮也白搭。这篇文章就把我这套 4 张表的 DDL 从建模思路、字段类型、约束策略到完整可跑的 SQL 脚本全部拆开讲适合正在设计学校管理系统数据表、准备数据库建模面试、或者需要一个靠谱初始化版本直接抄作业的同学。1. 整体建模怎么从“学校”两个字拆出 4 张表1.1 先画清楚实体关系再写建表语句很多人拿到需求第一反应是打开编辑器敲 CREATE TABLE我习惯先反推业务场景。学校管理系统里最核心的参与者就三类学生、教师、课程。学生要选课教师要授课课程被学生选择这三者天然形成关系。学生和课程之间是多对多关系一个学生可以选多门课一门课可以被多个学生选。关系型数据库里处理多对多最标准的方式就是拆出一张中间表也就是选课表。教师和课程之间则是一对多关系一个教师可以教多门课一门课通常只有一个主讲教师所以课程表里放一个 teacher_id 外键就够。这里有个常见误区有人会觉得选课表是多余的直接在学生表里加一个 course_ids 字段用逗号分隔存多个课程 ID。这种设计在早期看起来省事等你要统计某门课的选课人数、按课程查学生名单、或者存成绩的时候就会发现自己给自己挖了一整个坑。逗号分隔字段无法用 SQL 高效关联无法加外键约束数据一致性全靠应用层自觉所以拆表是唯一靠谱的方案。1.2 范式越高越好吗这个项目的平衡点三范式是建模的基本功第一范式保证字段原子性第二范式消除部分依赖第三范式消除传递依赖。学生表里只放学生自身属性教师表里只放教师自身属性课程表里只放课程自身属性选课表里只放学生、课程、成绩和选课时间这已经是标准的第三范式。但实际项目里我从来不会盲目追高范式反而会在设计阶段就想清楚哪个字段允许冗余。比如选课表里要不要冗余 student_name 和 course_name我的答案是这个阶段不要。冗余确实能让查询少做一次 JOIN但代价是更新时必须同步多个表一旦某学生改了名字所有历史选课记录里的名字都得跟着改漏改一处就是数据不一致。对 SchoolDB 这种量级的系统查询性能完全可以通过索引解决没必要提前引入一致性风险。后续如果真到了需要冗余才能扛住查询压力的阶段再通过定时任务或触发器去维护冗余字段也不迟。1.3 字段命名与类型约定先统一再动手这 4 张表虽然各自独立但整体设计上必须保持一致的约定否则后期维护会非常痛苦。我这边定的约定是表名和字段名统一用 snake_case表名用单数每张表必须有自增主键 id必须有 created_at 和 updated_at 两个时间字段所有状态字段用 TINYINT 而非布尔值或 ENUM所有字段必须写 COMMENT。命名看似小事实际上直接影响后续开发效率。搜一下网上那些老项目studentName、Student_Name、student_name 三种风格混在一起自动生成代码的时候简直想砸键盘。统一用 snake_case 之后MyBatis-Plus、JPA 这类框架做驼峰映射都不需要额外配置代码生成器直接输出就能用。COMMENT 更是刚需没有注释的字段表三个月之后你自己回来看都要猜半天更别说团队协作。2. 逐表拆解字段、类型与约束背后的取舍2.1 学生表把个人信息拆干净学生表是整套系统的基础字段不多但每个字段的类型选择都有讲究。学号 student_no 用 VARCHAR(20) 而不是 BIGINT原因是学号虽然看起来像数字但它根本没有数学意义而且很多学校的学号带字母、带前导零BIGINT 存不了前导零VARCHAR 才能完整表达。这里还有个潜在需求学号具有业务唯一性所以除了主键之外还必须给 student_no 加一个唯一索引作为候选键。姓名 name 用 VARCHAR(50)。有人会问 50 够不够国内姓名很少超过 10 个汉字就算考虑少数民族名字转写50 个字符也绰绰有余。性别 gender 用 TINYINT0 未知、1 男、2 女默认 0。为什么不用 ENUM(男,女)因为 ENUM 的枚举值扩展非常痛苦今天要加一个“保密”就得 ALTER TABLE 改表结构而且 Java 枚举映射、前端下拉框渲染最终都是拿数字或字符串做映射TINYINT 加 COMMENT 是最省心的方案。出生日期 birth_date 用 DATE入学日期 enroll_date 也用 DATE这两个字段不需要时分秒用 DATETIME 纯属浪费存储空间和索引性能。手机号 phone 用 VARCHAR(20)不要用 BIGINT因为手机号可能带 86 前缀、可能有分隔符而且永远不需要参与数值运算。邮箱 email 用 VARCHAR(100)备注 remark 用 VARCHAR(255)这些常规字段没什么争议。状态 status 用 TINYINT默认 10 禁用、1 在读、2 毕业。这个字段的价值在于支持逻辑删除学生毕业了或者退了学记录不能物理删掉因为历史选课、成绩、缴费记录都要挂在这个学生 ID 下面物理删除会引发一串级联问题。2.2 教师表职责与归属信息教师表的结构和学生表高度对称但多了职称和院系两个业务字段。教师工号 teacher_no 同样是 VARCHAR(20) 加唯一索引规则和学生表一致。姓名 name、性别 gender、手机号 phone、邮箱 email 的类型与学生表完全一致这里不重复解释。职称 title 用 VARCHAR(30)存“助教”“讲师”“副教授”“教授”这类文本。职称字段我纠结过要不要用 TINYINT 加字典表后来放弃了。字典表确实更规范改个职称名称不用动业务表但对这个小系统来说职称的可选值相当稳定VARCHAR 加应用层校验已经足够硬拆一张字典表反而增加 JOIN 复杂度。院系 department 用 VARCHAR(50)直接存“计算机学院”这种文本。这里同样有字典表的诱惑但和职称同理先保持简单。入职日期 hire_date 用 DATE状态 status 语义做了一点调整0 离职、1 在职、2 休假。为什么状态语义和学生表不一样因为业务语义不同学生关注在读状态教师关注在职状态没必要为了统一而强行套同一个编码。教师表目前没有外键因为教师本身是独立实体不依赖其他表存在。它的 ID 会被课程表引用成为被依赖方。2.3 课程表唯一课程编码与授课教师课程表是连接教师和学生的桥梁设计上要同时保证业务唯一性和外键关系的合理性。课程编码 course_code 用 VARCHAR(20) 加唯一索引编码规则可能是“CS101”“MATH202”这类格式由教务部门统一制定。课程名称 course_name 用 VARCHAR(100)足够容纳“数据结构与算法”这种长度。学分 credit 用 DECIMAL(3,1)这是一个容易踩坑的点学分可能是 0.5、1.5、2.0用整数类型存不了小数用 FLOAT 又存在浮点精度问题DECIMAL 才是精确小数的正解。DECIMAL(3,1) 最大能存 99.9单门课学分不可能超过这个范围。总学时 hours 用 SMALLINT UNSIGNED最大 65535足够覆盖任何课程。开课学期 semester 用 VARCHAR(20)存“2024-2025-1”这种字符串因为学期格式多样不是标准日期不适合用 DATE。课程类型 course_type 用 TINYINT0 必修、1 选修、2 公选。选课人数上限 max_students 用 INT UNSIGNED0 表示不限制这样一个字段同时表达了“限制”和“不限制”两种状态。状态 status 用 TINYINT0 下架、1 开放选课、2 已结课。这里最值得展开的是授课教师 teacher_id。它作为外键关联 teacher 表的 id但我没有把它设为 NOT NULL因为现实中存在“课程先创建、教师后分配”的场景。外键策略上用了 ON DELETE SET NULL意思是如果教师离职被物理删除课程记录仍然保留teacher_id 自动置空而不是把课程一起删掉。这一点很关键课程是教学资产不能因为教师离职就消失。2.4 选课表核心关系与约束选课表是整套系统里业务规则最密集的表也是最能体现 DDL 设计水平的地方。选课表的核心字段是 student_id 和 course_id两个都是 NOT NULL 的外键。成绩 score 用 DECIMAL(5,2)默认 NULL因为成绩是选课之后才录入的在选课那一刻不存在。DECIMAL(5,2) 最大能存 999.99有些学校平时分、期末分加权后可能超过 100 分4 位整数部分更稳妥。选课时间 select_time 用 DATETIME默认 CURRENT_TIMESTAMP记录学生是什么时候选的课。状态 status 用 TINYINT这里我设计了 0 已选、1 已退选、2 已结课。有同学会问既然退选了为什么不直接删掉记录因为业务上可能需要追溯“这个学生曾经选过这门课后来又退了”的事实比如教务纠纷、学分审核所以保留记录更安全。最关键的约束是复合唯一索引 uk_student_course (student_id, course_id)。这个索引表达了一条强业务规则一个学生对同一门课程只能有一条选课记录。注意只有 status 的业务语义是“永久选课记录”时这个唯一索引才成立。如果学校允许学生退选后重新选同一门课而且要求保留多次选课历史那这个唯一索引就必须去掉改由应用层在插入前检查“当前是否存在有效选课记录”。我在这套推荐默认方案里选择保留唯一索引因为大多数管理系统都不希望出现重复选课数据宁可退选时保留原记录也不能让同一学生同一课程出现两行。此外复合唯一索引还有索引加速的作用。它能够加速WHERE student_id ?的查询因为 student_id 是最左前缀也能够加速WHERE student_id ? AND course_id ?的查询这是选课场景最常见的查询模式。但它无法加速WHERE course_id ?因为 course_id 不是最左前缀。所以我又单独为 course_id 加了一个普通索引 idx_course_id用于“查某门课都有谁选了”这类反向查询。外键策略方面student_id 和 course_id 都用了 ON DELETE CASCADE。学生被物理删除时他的选课记录跟着删除课程被物理删除时选课记录也跟着删除。这个策略看着干脆但我在后面踩坑章节里会补充一个重要提醒如果成绩数据已经归档物理删除父表记录会把历史成绩也带走所以生产环境里更稳妥的做法是父表用逻辑删除让 CASCADE 几乎没有被触发的机会。3. 完整 DDL 脚本直接可用的初始化 SQL3.1 先建库统一字符集和排序规则建表之前先建库字符集和排序规则必须先定死不然后面所有表都会继承这个基础设置。字符集我选 utf8mb4很多人只知道它能存 emoji没意识到一个更重要的原因MySQL 里的 utf8 实际是 utf8mb3只支持基本多语言平面像一些生僻汉字、特殊符号它会存不进去或变成乱码。学生姓名、备注里完全可能出现这类字符所以直接用 utf8mb4 一步到位。排序规则我选 utf8mb4_0900_ai_ci这是 MySQL 8.0 的默认规则不区分大小写比较规则符合现代 Unicode 标准。如果服务器上还有其他数据库用了不同排序规则后面 JOIN 查询时会有麻烦这个在踩坑章节细说。3.2 四张表的完整 DDL下面是这套 SchoolDB 的完整初始化脚本按依赖顺序依次创建学生表、教师表、课程表、选课表。注意不能先建课程表再建教师表因为课程表的外键依赖教师表选课表必须最后建因为它同时依赖学生表和课程表。CREATE DATABASE IF NOT EXISTS school_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; USE school_db; -- 学生信息表 CREATE TABLE student ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, student_no VARCHAR(20) NOT NULL COMMENT 学号业务唯一标识, name VARCHAR(50) NOT NULL COMMENT 姓名, gender TINYINT NOT NULL DEFAULT 0 COMMENT 性别0未知1男2女, birth_date DATE DEFAULT NULL COMMENT 出生日期, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, email VARCHAR(100) DEFAULT NULL COMMENT 邮箱, enroll_date DATE DEFAULT NULL COMMENT 入学日期, status TINYINT NOT NULL DEFAULT 1 COMMENT 状态0禁用1在读2毕业, remark VARCHAR(255) DEFAULT NULL COMMENT 备注, 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 uk_student_no (student_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci COMMENT学生信息表; -- 教师信息表 CREATE TABLE teacher ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, teacher_no VARCHAR(20) NOT NULL COMMENT 教师工号业务唯一标识, name VARCHAR(50) NOT NULL COMMENT 姓名, gender TINYINT NOT NULL DEFAULT 0 COMMENT 性别0未知1男2女, title VARCHAR(30) DEFAULT NULL COMMENT 职称助教、讲师、副教授、教授等, department VARCHAR(50) DEFAULT NULL COMMENT 所属院系, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, email VARCHAR(100) DEFAULT NULL COMMENT 邮箱, hire_date DATE DEFAULT NULL COMMENT 入职日期, status TINYINT NOT NULL DEFAULT 1 COMMENT 状态0离职1在职2休假, 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 uk_teacher_no (teacher_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci COMMENT教师信息表; -- 课程信息表 CREATE TABLE course ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, course_code VARCHAR(20) NOT NULL COMMENT 课程编码业务唯一标识, course_name VARCHAR(100) NOT NULL COMMENT 课程名称, credit DECIMAL(3,1) NOT NULL DEFAULT 0.0 COMMENT 学分支持0.5、1.5、2.0, hours SMALLINT UNSIGNED NOT NULL DEFAULT 0 COMMENT 总学时, teacher_id BIGINT UNSIGNED DEFAULT NULL COMMENT 授课教师ID关联teacher.id, semester VARCHAR(20) DEFAULT NULL COMMENT 开课学期如2024-2025-1, course_type TINYINT NOT NULL DEFAULT 0 COMMENT 课程类型0必修1选修2公选, max_students INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 选课人数上限0表示不限, status TINYINT NOT NULL DEFAULT 1 COMMENT 状态0下架1开放选课2已结课, 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 uk_course_code (course_code), KEY idx_teacher_id (teacher_id), CONSTRAINT fk_course_teacher FOREIGN KEY (teacher_id) REFERENCES teacher (id) ON DELETE SET NULL ON UPDATE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci COMMENT课程信息表; -- 学生选课表 CREATE TABLE course_selection ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, student_id BIGINT UNSIGNED NOT NULL COMMENT 学生ID关联student.id, course_id BIGINT UNSIGNED NOT NULL COMMENT 课程ID关联course.id, score DECIMAL(5,2) DEFAULT NULL COMMENT 成绩0-100区间允许小数, select_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 选课时间, status TINYINT NOT NULL DEFAULT 0 COMMENT 状态0已选1已退选2已结课, 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 uk_student_course (student_id, course_id), KEY idx_course_id (course_id), CONSTRAINT fk_selection_student FOREIGN KEY (student_id) REFERENCES student (id) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT fk_selection_course FOREIGN KEY (course_id) REFERENCES course (id) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci COMMENT学生选课表;这个脚本直接放到 MySQL 8.0 环境里就能跑通。如果你的环境是 5.7唯一要注意的是 utf8mb4_0900_ai_ci 这个排序规则在 5.7 里不存在要改成 utf8mb4_general_ci其他部分都能兼容。3.3 几个容易被忽略的 DDL 细节主键统一用 BIGINT UNSIGNED AUTO_INCREMENT有人会问为什么不用 INT。INT 最大约 21 亿对学校系统来说看起来够用但一旦系统长期运行、数据迁移、测试环境刷数据INT 的主键很容易逼近上限到时候扩表类型要锁表非常痛苦。BIGINT UNSIGNED 最大约 1844 亿亿基本不用担心用尽。而业务编码字段如 student_no、course_code 虽然也唯一但它们是业务标识不适合当主键因为业务标识可能随规则调整而变化一旦变更所有外键引用都要跟着改。自增主键有两个进阶参数值得知道。AUTO_INCREMENT 可以设置起始值比如AUTO_INCREMENT 10001让学号从 10001 开始和业务编码对齐但这只影响初始值不影响后续使用。另一个参数是自增步长一般不用动集群分片时才需要调整。存储引擎统一 InnoDB这是 MySQL 8.0 的默认引擎支持事务、外键、行级锁、MVCC。MyISAM 虽然查询快一点但不支持事务和外键数据一致性没有保障现在没有任何理由选择它。所有字段都写了 COMMENT这是最容易被新人忽略的细节。注释就是给未来的自己看的三个月后你回来看这张表有没有注释的体验完全是两个世界。更重要的是很多代码生成工具能从 COMMENT 自动生成字段说明文档省掉后续补充设计文档的工作。关于 CHECK 约束我多说一句。MySQL 8.0.16 之前CHECK 约束只是解析器层面的语法不会真正校验数据所以很多老项目习惯用 TINYINT 加应用层校验来替代。8.0.16 之后 CHECK 真正生效了比如可以给 score 加CHECK (score 0 AND score 100)但我不建议在这个阶段加太多 CHECK因为学校成绩规则未来可能调整比如出现加分项、缓考标记数据库层的硬约束反而会成为业务灵活性的绊脚石。我倾向于把灵活的规则放在应用层数据库只保证最基础的完整性。4. 建表阶段最容易踩的坑替你趟一遍4.1 重复选课为什么拦不住我见过一个真实案例某开发同学在选课表上没加唯一索引应用层插入前先查一遍有没有重复选课结果上线第一天就出现了同一个学生对同一门课的两条选课记录。原因是并发两个请求同时到达都先执行了 SELECT 检查都发现没有记录然后都执行 INSERT最终插入了两条。数据库层的唯一约束才是最后的防线应用层的先查后插在并发场景下根本不可靠。这也是为什么我在选课表上坚持加 UNIQUE KEY uk_student_course (student_id, course_id)。有了它即使应用层逻辑写漏了数据库也会硬性拒绝重复插入报 duplicate key 错误应用层捕获这个错误做友好提示就行。数据库的约束越严格应用层越好写。4.2 外键约束导致的建表顺序连锁问题外键不是建完就能用的它要求被引用的表必须先存在。如果你先执行课程表的 CREATE TABLE再执行教师表的 CREATE TABLEMySQL 会直接报错提示无法添加外键约束因为引用的表不存在。我当时第一次写这套脚本时也踩过后来养成一个习惯建表按依赖顺序来先建无外键的表再建有外键的表。删除表的顺序恰恰相反要先删子表再删父表。如果直接 DROP TABLE teacher而 course 表还引用着它MySQL 会报错说存在外键约束依赖。所以在初始化脚本和清理脚本里顺序都要设计好。还有一个关于 CASCADE 的提醒。选课表里 student_id 和 course_id 都设了 ON DELETE CASCADE这意味着物理删除学生或课程时选课记录会自动删除。听起来很省事但如果成绩已经录入了删掉选课记录就等于删掉了历史成绩这是不可逆的。我在生产项目里更推荐父表用逻辑删除也就是只更新 status 字段不真正 DELETE这样 CASCADE 几乎不会被触发历史数据始终保留。4.3 时间字段默认值低版本 MySQL 的坑和 2038 问题时间字段的默认值在不同 MySQL 版本里有明显差异。5.6.5 之前的 DATETIME 不支持 DEFAULT CURRENT_TIMESTAMP只能用 TIMESTAMP 或者靠应用层插入时赋值。如果团队里有老库写 DDL 前一定要确认版本不然后面上线才发现默认值没生效。另一个是 TIMESTAMP 的 2038 问题TIMESTAMP 类型用 4 字节存储范围到 2038 年就溢出了。DATETIME 用 8 字节范围大得多。所以我在所有表里统一用 DATETIME既支持默认值又没有 2038 问题。4.4 字符集和排序规则不一致JOIN 直接索引失效字符集不统一最典型的症状是中文乱码。表 A 是 utf8mb4表 B 是 utf8mb3两边 JOIN 的时候 MySQL 为了比对字符串会做隐式字符集转换结果索引用不上全表扫描数据一多就慢得离谱。更隐蔽的是排序规则不一致即使两张表都是 utf8mb4一张用 utf8mb4_0900_ai_ci另一张用 utf8mb4_general_ciJOIN 时同样可能触发隐式转换。因此我在建库时就统一了整个数据库的字符集和排序规则所有表继承同一个设置杜绝这类问题。如果你接手的项目里已经出现这种情况可以用 ALTER TABLE 统一排序规则ALTER TABLE course_selection CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;执行前先评估表数据量和锁表窗口线上大表不能直接这么干要在低峰期操作。4.5 状态字段用 ENUM 的扩展之痛设计学生表时我就提到过性别没用 ENUM。这里展开说原因ENUM 的枚举值是写死在表结构里的今天需要增加一个枚举值就得执行 ALTER TABLE对大表来说可能锁表很久。而且 ENUM 在 MySQL 内部是按整数存储的排序和比较规则也不直观跨数据库迁移时更是麻烦。TINYINT 加 COMMENT 的方案则灵活得多改变枚举含义只需要更新 COMMENT数据完全不用动。业务层面加一个新的数字映射比如性别加一个 3 代表“其他”只需要 INSERT 时用新值不用改表结构代码里加一个判断就行。前端下拉框配置在配置中心或字典表里随时可以加选项。4.6 成绩精度成谜FLOAT 惹的祸成绩字段用 FLOAT 还是 DECIMAL看起来只是类型选择实际涉及精度问题。FLOAT 是浮点数存储的是近似值0.1 在二进制里根本表示不精确。如果某学生两次考试成绩都是 85.5用 FLOAT 存完再读出来可能是 85.5 也可能是 85.5000001做等值比较、排序、求平均时都会出现反直觉的结果。成绩、学分、金额这类需要精确计算的业务字段一律用 DECIMAL。选课表的 score 用 DECIMAL(5,2)既满足 0 到 100 的常规范围又留足了超出 100 分的扩展空间。课程表的 credit 用 DECIMAL(3,1)支持半分制学分。这个坑我建议所有做学校系统的同学提前避开。这里顺便整理一份常见的字段类型误用速查业务含义错误用法推荐用法原因学号/工号/课程编码BIGINTVARCHAR(20)可能带字母、前导零无数学意义手机号BIGINTVARCHAR(20)可能带前缀和符号不参与运算性别ENUM(男,女)TINYINT COMMENTENUM 扩展枚举需改表结构成绩/学分FLOAT / DOUBLEDECIMAL浮点数有精度损失DECIMAL 精确出生日期/入职日期DATETIMEDATE无需时分秒DATE 更节省状态字段CHAR(1)TINYINT数字编码扩展性更好学期DATEVARCHAR(20)学期格式多样不是标准日期做 SchoolDB 这 4 张表的结构设计时我的体会是DDL 从来不是“能建出来就行”的体力活每一处类型选择、每一条约束、每一个索引背后都是对业务规则的提前表达。把结构想透了后面写业务代码会顺畅很多很多数据层面的脏问题在源头就被拦掉了。这套脚本你直接拿去用没问题但更建议你照着思路重新审视一遍自己项目的表结构尤其是外键策略和唯一索引这两个地方往往是最容易出问题的。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑