简介:数据库设计是信息管理系统开发的核心环节,关系型数据库借助实体联系模型完成概念结构设计,并通过关系模式转换将业务实体映射为可实施的表结构。在汽车租赁这类业务场景中,从预订、派车、还车到费用结算,都需要围绕数据字典与规范化理论构建完整的存储结构。本文从概念结构设计出发,梳理局部E-R图合并与逻辑结构转换的关键方法,结合实际落库需求,介绍如何用MySQL完成九张核心表的建表与数据维护,并针对常见类型选型、冗余字段和时间精度问题给出可操作的排查建议,为数据库课程设计与中小型租赁系统开发提供一份能直接参考的实践指南。
1. 汽车租赁系统数据库设计:一份能撑住课程设计答辩的完整资料
这份汽车租赁系统数据库设计文档,最难得的是它把一条完整设计链路从头走到尾:从电话、前台、网上三种预订方式切入做需求分析,到九张数据字典表的字段与长度定义,再到局部 E-R 图合并、关系模式转换,最后落到数据载入和运行维护。对正在做数据库课程设计的人来说,它可以直接当设计底稿;对已经在写租赁业务后端的人,九张表的字段结构也能省掉一大半建模时间。文档的目标很明确:让你把一个关系数据库从需求分析到实施维护的每个阶段都能写出来、画出来、讲出来。适合数据库原理实践、毕业设计前期建模,也适合想快速搭一套租赁业务库的开发者。按三个月的完成期限来倒推,每一阶段产什么文档、画什么图,这份资料里都有对应素材。
2. 需求分析与数据字典:四大模块如何落成九张表
2.1 先理业务主线,再谈建表:预订到还车的需求映射
文档的需求分析部分没有一上来就扔表结构,而是先描述了业务场景:客户可以打电话、前台登记、网上下单来预订车辆,系统要能保存预订申请单、保留客户历史记录;工作人员负责处理申请,技术人员提交每辆车的检修状态,作为批准请求的参考。这段容易被当成流程文字跳过去,但它其实是整个库的结构地基。我拿到这类文档,第一步就是把业务流程拆成“谁、在什么节点、产生什么数据”:
- 客户预订:产生预订申请单,里面要有客户信息、期望车型、租用时间;
- 工作人员处理:把申请转为正式租赁记录,记录实际派发的车辆和司机;
- 技术人员检修:更新车辆状态,确定车辆是否可租;
- 还车与续租:生成还车、续租记录,更新车辆状态和客户历史记录。
这样拆完再对照文档的四大模块——基本数据维护、基本业务、数据库管理、信息查询,模块和业务就能一一对上。基本数据维护模块负责客户、车辆、租赁信息的录入和修改;基本业务模块承载预订、续租、还车、检修状态的流转;数据库管理模块统一管理客户、工作人员、车辆信息并登记租赁情况;信息查询模块面向工作人员提供车辆、客户、租赁记录的检索。模块和表的关系可以做成下面这样一张对照表,写课程设计说明书时直接能用:
| 功能模块 | 主要操作 | 对应的数据字典表 |
|---|---|---|
| 基本数据维护 | 客户、车辆、租赁信息录入与修改 | 公司、汽车、客户、会员类型 |
| 基本业务 | 预订、续租、还车、检修状态处理 | 租赁、雇佣、司机、车辆保险 |
| 数据库管理 | 统一管理和登记 | 全部九张表 |
| 信息查询 | 按条件查车、查客户、查租赁 | 客户、汽车、租赁、公司 |
需要特别留意一点:文档说“技术人员可以保存对车辆检修的结构”,但九张表里并没有单独的检修表,检修结果实际落在汽车表的 State 字段,用“在库 / 不在库”来表示。这种压缩是课程设计里的常见取舍。如果答辩被问到“检修记录存在哪”,你可以回答用状态位管理,也可以主动补一张检修记录表。这个点我在第 5 章会再展开,属于典型的需求分析与物理实现之间的落差。
2.2 九张数据字典表逐表拆解
数据字典一共九张表:公司、汽车、车辆保险、保险公司、客户、会员类型、司机、租赁、雇佣。每张表都定义了存储代码、类型、长度和备注,对课程设计来说非常够用。先看总览:
| 表名 | 主键 | 关键字段 | 说明 |
|---|---|---|---|
| 公司 | Fno | Fname、Ftell、Faddress、Femail、Ffax、Fzip | 租赁公司基础资料 |
| 汽车 | Cno | Cname、Ctype、Colour、Cmileage、Cprice、Oprice、State | 车辆档案与租赁定价 |
| 车辆保险 | Bno | Bname、Cnumber、Bdate、Btime、Bmoney、Dname | 车辆投保记录 |
| 保险公司 | Dname | Daddress、Dtel1、Dtel2 | 保险合作方 |
| 客户 | Kno | Kname、Knumber、Ksex、Ktel、Klicense、Lnumber 等 | 客户档案 |
| 会员类型 | Mno | Mmane、Mlevel | 会员等级 |
| 司机 | Snumber2 | Sname、Ssex、Syear、Sold、Sclass、Stel | 司机档案 |
| 租赁 | Znumber | Kname、Cname、Cnumber、Sdate1、Sdate2、Smoney1、Smoney2 | 租赁流水 |
| 雇佣 | 复合键 | Gdate1、Gdate2、Gmoney、Snumber2 | 客户雇佣司机记录 |
公司表是典型的基础资料表,字段带 F 前缀,编号、名称、电话、地址、邮箱、传真、邮编,全是租赁公司的静态信息。汽车表是核心资源表,车牌号 Cno 做主键,Cprice 是每小时租赁价格,Oprice 是逾期每小时价格,State 区分在库和不在库,后续所有租赁业务都围绕这张表展开。客户表字段最多,除了基本身份信息和联系方式,还把有无驾照、驾驶证编号、类型、家庭住址、工作单位全放了进来,甚至把取车时间、预定使用时间、还车时间这些业务字段也塞进了客户表。
这种把业务时间冗余进客户表的做法,常见于早期课程设计:好处是打印客户信息单时一次查全,坏处是同一客户多笔租赁时,这三列只能保留最近一次,历史记录存不住。租赁表则是整份设计的业务核心:流水号 Znumber、客户姓名、身份证号、电话、车名、车辆类型、车牌号、司机名、司机工号、起租时间、还租时间、押金、租金、是否投保,一条流水把一次租车业务涉及到的参与方信息全部快照进来。雇佣表保存客户自愿雇司机的记录,包含开始时间、结束时间、佣金、司机电话。车辆保险表和保险公司表处理车辆投保关系,投保车辆通过 Cnumber 关联回汽车表。
2.3 数据字典的选型逻辑:char、long、date 各管一段
读数据字典不能只看字段名,还要理解类型和长度的选择原因。文档里大量使用 char 定长,比如车牌号、编号、名称、电话,长度基本在 10 到 50 之间。定长 char 在等值查询上性能稳,尤其车牌号这种长度固定的字段用 char 完全合理。但姓名、地址这类内容长度波动大的字段,用 char 就意味着每次都要按最大长度存,配合空格补齐,后续查询容易出问题。
价格字段用 long 而不是 float,这个选择很直接:租金、押金、佣金都是金额,浮点数会有精度误差,整数类型在运算和存储上更省心。代价是如果租金带小数,比如每小时 37.5 元,long 就撑不住了,实际落地时我一般会换成 decimal(10,2)。时间字段在车辆保险、租赁、雇佣几张表里用了 date,长度 8,精确到日期。但租赁表备注里写的是“精确到分”,说明业务上还车时间需要到分钟级,这点在物理设计时要注意,date 只到天,满足不了“精确到分”,要改成 datetime。原文用 date 是为了教学简化,实际做系统时得把精度提上去,这个我在第 5 章会当作一条踩坑记录展开。
3. E-R 图与关系模式转换:实体联系怎么变成可建表的字段
3.1 局部 E-R 图、合并 E-R 图与“逐步扩张”的集成过程
文档在概念结构设计部分列出了四种方法:自顶向下、自底向上、逐步扩张、混合策略。实际文档展示的路径,是先把每个实体单独画 E-R 图——公司、汽车、车辆保险、保险公司、客户、会员、司机各一张,最后通过租赁和雇佣两类联系合并成一张全局 E-R 图。这个做法在教科书里叫“局部视图集成”,先设计局部视图,再逐步合并,本质上是自底向上和逐步扩张的混合。
数据抽象的三要素“分类、聚集、概括”在这里的具体体现是:实体按业务角色分类,客户、司机、公司是独立角色;车辆保险和汽车是“部分—整体”的聚集关系;保险公司和公司都可以看成机构,但没有强行抽象成父类。合并时的关键动作是检查三类冲突:命名冲突、属性冲突、结构冲突。比如客户表里“编号 Kno”和司机表里“身份证号 Snumber1”都指向自然人身份,但命名不同,合并后不能直接把两个实体揉成一个表,因为客户和司机在业务上确实可以不是同一个人。
这里有个值得注意的细节:合并 E-R 图里会员实体只有“会员编号、用户名、级别”三个属性,并没有用一条线连接到客户。文档的隐含设计是会员和客户各自独立。这就有个隐患——如果会员积分和租赁消费挂钩,会员表缺乏外键指向客户编号,业务层要额外维护关联。做课程设计时如果被问到“会员和客户是什么关系”,最好能主动回答清楚,否则老师很容易把它当成一个设计漏洞来看。
3.2 关系模式转换三规则:实体、联系和依赖怎么落表
文档给出了从 E-R 图到关系模式的完整结果:公司、汽车、车辆保险、保险公司、客户、会员、司机、租赁、雇佣九个关系模式。这套转换覆盖了三种典型情况,搞懂这三条,任何课程设计都能套:
实体直接转表。公司、汽车、客户、保险公司属于这类,每个实体属性变成列,标识属性选为主键。
一对多联系转外键。车辆保险和汽车之间的“投保”联系,车辆保险表里放一个 Cnumber 引用汽车表主键;保险公司和车辆保险之间,则把 Dname 放到车辆保险表里做外键。这样车辆保险表同时关联了汽车和保险公司两张主表。
联系本身有属性时,转成独立关系模式。最典型的是租赁和雇佣。租赁联系带押金、租金、起租 / 还租时间、是否投保这些属性,如果只靠外键关联而不建表,属性无处安放;雇佣联系带佣金、开始时间、结束时间,同样需要独立表。这就是文档里租赁表、雇佣表单独存在的原因。
还需要注意:租赁表把客户姓名、身份证号、电话、车名、车牌号、司机姓名、司机工号全放了进去。按规范化常理这些应该用外键,但文档选择直接在业务流水里冗余一份。课程设计里这是有争议的做法,但如果答辩理由充分,比如“避免多表 join,因为租赁单要打印快照”,通常也说得过去。关键是别把冗余写成“随手复制的”,要有意识地在设计说明里注明用途,这是文档里值得学的一处表达。
3.3 租赁与雇佣联系表:为什么流水号必须独立
租赁表的主键是 Znumber 流水号,而不是客户编号 Kno 或车牌号 Cno。这个选择很正确:同一个客户可以租多次,同一辆车可以被不同客户租,客户和车之间是多对多,任何单方的主键都撑不起来,必须引入一个业务流水号来标识“一次租赁”。流水号随业务生成,不管用户和车辆怎么变,一次租车全过程都对应唯一一条记录。
租赁表里还带 Sdate1 起租时间和 Sdate2 还租时间,备注“精确到分”,例子是 201110291551 这种 14 位字符串。押金 Smoney1 在还车时退还,租金 Smoney2 是应收费用,是否投保 Sbaoxan 用 char 4 存,控制租车时是不是连保险一起生效。这几列都是“租赁”这个联系自身的属性,不是客户属性也不是汽车属性,所以必须放在租赁联系表里,这正是联系实体建模的典型写法。
雇佣表和租赁表的结构逻辑类似:客户可以雇佣司机,司机可以为不同客户服务,所以也需要一张独立表记录开始时间、结束时间、佣金。但雇佣表没有定义单一主键,靠“客户姓名 + 身份证号 + 司机工号 + 开始时间”一组字段去唯一识别一条雇佣记录。这种复合键设计在关系模式上能跑通,但实际建表时最好补一个自增 id,否则后面做更新、删除时写 where 条件会很痛苦。这个我会在第 5 章再讲。
4. 概念结构与逻辑结构设计:从全局视图到规范化建表
4.1 四种概念结构设计方法:为什么这份文档适合“逐步扩张”
概念结构设计的方法论在文档里列得很清楚:自顶向下、自底向上、逐步扩张、混合策略。很多人把这四个名词背下来就完事,但答辩时老师更想听到的是“你为什么用这个”。自顶向下适合业务边界清晰、模块层级明确的系统,从全局框架逐层细化到实体;自底向上适合模块独立、各模块内部结构复杂的系统,先把局部 E-R 图做扎实再合并;逐步扩张则适合从核心业务出发,先画出最关键的租赁 E-R 图,再往外扩出客户、车辆、保险、会员。
这份文档实际走的是“局部 E-R 图逐张设计、最后合并”的路线:先独立画公司、汽车、车辆保险、保险公司、客户、会员、司机的实体图,再用租赁和雇佣联系把它们串成一张总图。这更接近自底向上加逐步扩张的组合。课程设计场景里这个路线容错率最高——即使中途某张局部图有错,合并时也只影响局部,不用推翻重来。
数据抽象的四种手段——分类、聚集、概括、选择局部应用——在文档里的痕迹也很清楚:客户、司机、公司按角色分类;车辆保险挂在汽车上属于聚集;保险公司的 Dname 同时被车辆保险表引用,属于概括上的共享引用。写设计文档时把“用了哪种抽象”写进文字说明,整篇的层次感会立刻不一样。如果把这个阶段单独写成说明书章节,建议把每张局部 E-R 图对应的业务场景列出来,再写合并时解决了哪类冲突,这部分内容在答辩中几乎必问。
4.2 逻辑结构设计:关系模式规范化与“刻意冗余”的边界
逻辑结构设计分两步:把 E-R 图转换成关系模式,然后再做优化。文档给出的关系模式图已经是转换后的结果,但转换完不等于可以直接建库,还要过一遍规范化检查。把租赁表单独拿来看:Znumber 流水号决定客户姓名、身份证号、电话、车名、车牌号、司机姓名、司机工号、起租时间、还租时间、押金、租金,单看这一层,租赁表满足大部分字段对主键的依赖。
但把“客户姓名依赖身份证号”“车牌号依赖车辆编号”这种依赖关系展开时,租赁表里确实存在传递依赖——客户姓名并不直接依赖流水号,而是依赖客户身份证号,而身份证号又依赖流水号。严格按第三范式,这些字段应该拆到客户表、车辆表,租赁表只保留外键。文档选择不拆,保留冗余快照,这是典型的“以查询效率换更新一致性”。
课程设计里这个取舍可以接受,但必须在设计说明里写明:租赁单打印时需要一次查出所有参与方信息,冗余字段可以避免频繁 join。否则老师一句“你这里是不是没做规范化”就能问住。反过来说,如果想把设计做得更规范,可以在租赁表里只留 Znumber、Kno、Cno、Snumber2 和业务属性,客户名、车牌名全部 join 出来。文档的价值在于它把“要冗余哪些字段”已经标清楚了,照着删掉重复列就能快速得到一个接近第三范式的版本,两边都能落地。
4.3 物理结构设计与数据载入:索引、约束和实施顺序
文档在实施维护部分明确提出三个约束:租借和归还信息必须及时更新;信息无差错存储在主服务器上;输出要简捷、快速、实时、准确。这几句话在课程设计里容易被当成口号,但落到物理结构设计阶段是有具体操作的。
索引和约束属于物理结构设计范畴,文档没有展开,这部分得自己补。我的做法是:租赁表的 Znumber 建主键索引,Kno 建普通索引,Sdate1、Sdate2 建联合索引,支撑按时间段查流水;客户表的身份证号建唯一索引,防止同一人重复建档;汽车表的 State 字段建普通索引,方便快速筛在库车辆。外键约束全部开启,保证 Kno、Cno、Snumber2 引用有效。存储引擎用 InnoDB,字符集统一 utf8mb4,避免中文乱码。
数据载入顺序也有讲究。先载入基础表:公司、保险公司、会员类型、汽车,它们是其他表的引用依据;再载入客户、司机,这两张表不依赖业务流水;最后载入车辆保险、租赁、雇佣,因为它们的字段里带着前两类表的主键或冗余信息。顺序反了,外键约束会让你插入失败,这是新手最容易踩的坑。载入完成后还要跑一遍运行评价:统计表行数是否匹配、外键校验是否通过、常用查询的执行计划是否走索引,这些做完才算完成“数据库实施”这一步。文档里提到的“评价、调整、修改方法”,对应到实操就是这几件事。
5. 实施运行避坑:常见问题与排查记录
把这份文档的设计落地成真实数据库时,会遇到一批和字段类型、冗余、权限、时间精度相关的典型问题。下面五条都是复现这类课程设计时真实踩过的,每一条都按现象、原因、解决三个层次说清楚。
5.1 身份证号用 int(20) 存:数值溢出与尾号 X 丢失
现象:按数据字典把客户身份证号 Knumber 建为 int、长度 20,插入 18 位身份证号时报“Out of range value”,或者能插入但读出时末两位变成 0;身份证号带 X 的更惨,直接插入失败。
原因:int 类型在 MySQL 里最长 10 位数字且有符号上限 21 亿多,18 位身份证号远超上限,要么报错,要么被数据库做隐式截断。更关键的是,身份证本质是“编号”,包含数字和校验位 X,根本不是数值,用 int 从建模上就错了。
解决:把 Knumber 改成 char(18),如果要兼容统一社会信用代码,可以放宽到 varchar(20) 并加唯一索引。连带影响是租赁表、雇佣表里所有引用身份证号的字段要一起改类型,不然外键对不上。建表前先在数据字典上做一轮“数值类型只用于金额和数量,编号一律字符串”的检查,能省掉后面一串迁移。
5.2 char 定长字段:姓名对不上的查询玄学
现象:客户姓名 Kname 建为 char(20),插入“张三”后执行 where Kname = '张三' 查得到,但用 group by 统计客户时,莫名多出带空格的版本;换到 Oracle 或 PostgreSQL 里,同样的等值查询偶尔返回不了结果。
原因:char 是定长类型,存储不足 20 位时自动补空格。MySQL 比较字符串时默认忽略末尾空格,所以等值查询能命中;但 group by 和 distinct 处理时会把补空格后的值当成独立分组,于是出现“张三”和“张三 ”两个组。Oracle 里 char 比较不忽略空格,等值查询就可能查不到。
解决:姓名、地址、邮箱这类长度不固定的字段,建表时直接用 varchar,别学文档里的 char(20)。车牌号、编号这类确实定长的字段保留 char 没问题,但涉及比较时建议统一用 trim 包裹,或者在应用层把输入值 trim 一遍再进 SQL。那份数据字典里 char 用得多,落到 MySQL 还能容忍,落到其他数据库就要逐个字段排查。
5.3 租赁表冗余字段:同步更新引发的“幽灵数据”
现象:客户改了手机号或姓名后,租赁表里历史记录的 Ktel、Kname 没跟着变,统计某客户租车次数时数字和客户档案对不上,出现两条看起来像同一个人但名字不同的记录。
原因:租赁表故意冗余了客户、车辆、司机信息,文档没有给出同步机制。冗余字段一旦建立,就额外多了一处需要维护的数据副本,任何主表更新都要求副本保持一致,否则就是更新异常。
解决:两种路线。要保冗余,就在客户表、汽车表上建 after update 触发器,客户信息变化时同步刷租赁表里的 Kname、Ktel;要讲规范,就把租赁表里的 Kname、Ktel、Cname 等删掉,只留 Kno、Cno、Snumber2 外键,查询时 join 客户表和汽车表。课程设计场景里我一般推荐后者,但为了兼容文档“租赁单免 join”的出发点,折中方案是保留一次快照,只存 Kname 和 Knumber,让客户主键做唯一依据。答辩时主动说出这个取舍,比被老师问住要体面得多。
5.4 权限控制名存实亡:管理员与工作人员共用一个账号
现象:文档安全要求写得很明确——管理员可管理和修改客户、租借信息库,工作人员只享有租赁信息库的部分修改权限。实际实现时却把所有使用者接到同一个数据库账号上,工作人员随手就能 drop 表。
原因:安全要求只停留在文档层面,实施阶段没在数据库里做角色区分。程序登录只管“能进系统”,数据库账号没分层,等于把前门锁了后门开着。
解决:按 admin、staff、guest 三个维度建角色。admin 角色对客户、租赁、员工主数据有增删改和 DDL 权限;staff 角色只对租赁信息相关表授权 insert 和 update,select 需要扩大到客户和车辆表以便日常查询;访客角色只读。MySQL 里直接用 create role 加 grant 就完成,建库脚本里就要带上这句,不要等上线再补。文档里写“工作人员只享有部分修改(写入与读出)”,对应的就是只授权插入和更新,不授权删除和建表。
5.5 时间“精确到分”却用 date 存:时长计算的连环锅
现象:建表时按文档把起租时间 Sdate1 设为 date,插入数据后发现只能存到“2011-10-29”,分钟信息直接没了;要算“租了多久、逾期多久”时,要么报错要么结果不准。
原因:date 类型只精确到天,文档备注说要“精确到分”其实是业务需求,但物理设计时没把类型升到 datetime 或 timestamp,字段精度和业务需求脱节。
解决:起租、还租、保险投保时间、雇佣开始结束时间全部用 datetime,统一格式到“YYYY-MM-DD HH:MM:SS”。写入时由程序格式化到分,查询时长用 timestampdiff 或 datediff 计算。另一个坑是时区:MySQL 连接串不设 serverTimezone 时,datetime 读写可能出现 8 小时偏移,连接参数里要固定时区。文档里那个“201110291551”只能当输入示例,不能当真去建 char 字段。
5.6 建库前的自检清单
把上面五条汇总成一张检查清单,我每次拿到类似文档做落地前都会过一遍:
| 检查项 | 自检要求 |
|---|---|
| 编号类字段 | 一律字符串类型,不用 int / bigint |
| 定长 / 变长 | 姓名地址邮箱用 varchar,车牌号编号用 char |
| 冗余字段 | 确认是保留快照还是外键,注明同步机制 |
| 权限角色 | admin / staff / guest 角色和授权落在建库脚本里 |
| 时间精度 | 凡备注“精确到分”一律 datetime,date 只用于日期口径 |
另外,文档里提到的“技术人员检修状态”没有独立表这件事,我的建议是:如果课程设计老师较真,你就补一张 vehicle_inspection 表,字段可以很简单,车辆编号、检修日期、检修人、结论、下一保养日期,然后用外键关联汽车表。这样需求分析里的“技术人员可以保存检修结构”就有了明确落点,比硬说“用 State 字段够用”更稳。
6. 把文档还原成 MySQL 库:三张核心表的建表脚本与验证习惯
文档本身是 Word 版设计材料,真正要让答辩老师信服,最好把它变成一个能跑的库。我拿到文档后的做法是:第一件事不是抄字段,而是把总览表里的九张表整理成建表脚本,然后按“先基础表、后业务表”的顺序执行。以 MySQL 8 为例,客户、汽车、租赁三张核心表的骨架大概是这样的:
-- 客户表:身份证号用 char(18),姓名用 varchar,避开 int 溢出和 char 补齐 CREATE TABLE customer ( kno CHAR(10) PRIMARY KEY, kname VARCHAR(20) NOT NULL, knumber CHAR(18) NOT NULL UNIQUE, ksex CHAR(4) DEFAULT '男', ktel VARCHAR(20), klicense CHAR(4) DEFAULT '有', lnumber VARCHAR(30), lkind VARCHAR(20), kaddress VARCHAR(50), kwork VARCHAR(50) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 汽车表:价格用 decimal(10,2),状态字段保留“在库/不在库” CREATE TABLE car ( cno CHAR(10) PRIMARY KEY, cname VARCHAR(20) NOT NULL, ctype VARCHAR(20), colour VARCHAR(20), cprice DECIMAL(10,2) NOT NULL, oprice DECIMAL(10,2) NOT NULL DEFAULT 0.00, state CHAR(10) DEFAULT '在库' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 租赁表:用外键关联客户和车辆,避免字段冗余带来的更新异常 CREATE TABLE rental ( zno BIGINT AUTO_INCREMENT PRIMARY KEY, kno CHAR(10) NOT NULL, cno CHAR(10) NOT NULL, sdate1 DATETIME NOT NULL, sdate2 DATETIME, deposit DECIMAL(10,2), rent_fee DECIMAL(10,2), insured CHAR(4) DEFAULT '否', FOREIGN KEY (kno) REFERENCES customer(kno), FOREIGN KEY (cno) REFERENCES car(cno) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;这里把文档里的客户姓名、身份证号从租赁表抽走,改成外键 kno,是为了避开 5.3 节的更新异常;车牌号用 char(10),姓名地址用 varchar,身份证用 char(18),避开 5.1 和 5.2 的两个雷。租金从 long 换成 decimal(10,2),支持小时单价带小数。建完表后我会强制跑三件事:第一,按先客户、汽车、后租赁的顺序插入测试数据,验证外键约束生效;第二,模拟一次还车,update car set state='在库' 同时 update rental set sdate2=now(),确认状态和时间同步;第三,执行一条带 join 的查询,统计某辆车累计出租次数和租金收入,验证索引和关联没有隐藏问题。
从那以后,我每次拿到课程设计类的数据库文档,都会先做一轮“类型审查”,把 int 存编号、char 存姓名、date 存分钟这三类问题在动工前扫掉,再把九张表按先基础后业务的顺序建出来跑通一次。这套流程看着笨,但能帮你避开绝大多数答辩现场的翻车。希望帮到你。
本文还有配套的精品资源,点击获取