
简介一份面向数据库课程实践的校园二手交易系统课程设计报告完整记录从需求分析、概念模型、逻辑模型到物理模型的设计全过程并包含系统实现与个人工作总结适合正在完成数据库课程设计或需要参考完整设计流程的学生。报告基于PowerDesigner建模在物理模型中纳入了约束、视图、触发器、存储过程、安全管理、恢复方案与事务设计等对象同时提供了基于SQL Server和Visual Studio .NET的数据库开发思路。资源包共1个doc文件大小约1.72MB文档结构清晰除主体报告外还附有需求说明书及多阶段建模文档说明。已有1572人学习下载可作为数据库设计实战参考帮助读者理解E-R模型转换、关系规范化及数据库实施规划等关键环节。1. 校园二手交易系统这份课程设计从 E-R 图到 T-SQL 的完整链路如果你正在找一份能直接对着复现的数据库课程设计参考这份《校园二手交易系统》报告值得仔细看一遍。它不是那种贴几张截图、写几句总结的糊弄文档而是把数据库设计的完整流程——需求分析、概念模型、逻辑模型、物理模型、SQL Server 实施到备份恢复策略——从头到尾走了一遍。说实话这份报告的技术深度不算惊艳但它最大的价值在于链路完整每个阶段该产出什么、用什么工具、脚本怎么写都有真实可抄的样例。适合正在做课程设计的学生用来对照自己的进度也适合刚接触 SQL Server 的开发者拿里面的 T-SQL 练手。我拆完这份文档最大的感受是数据库设计的坑它基本都踩过而且把补救过程也写下来了这部分比很多教材案例更有参考价值。2. 从摆摊场景到三层模型需求分析和 E-R 图怎么做才不乱2.1 需求分析先搞清楚要管哪些实体再谈建表这份报告的选题灵感来自校园里无人看管的二手摊位扫码付款、自己拿货。这个场景落到数据库里核心要解决的其实就三件事谁在卖、谁在买、卖什么。报告把用户分成买家和卖家两张表商品拆成教材、文具、生活用品三类每类商品配一张选择表记录交易关系。这个拆分方式在课程设计里很常见也很稳妥——它对应的是典型的“用户-商品-订单”三段式只不过订单被简化成了选择表。需求分析阶段最容易犯的错是一上来就画表结果画到一半发现漏了关键字段。我一般会先列业务规则再反推实体和属性。这份报告的做法可以借鉴它先定主架构写明“管理员为用户创建信息”“用户登录后可作为买家或卖家”再逐条列每个表的字段说明。比如买家表里“手机号唯一、姓名唯一”这种约束就是在需求阶段确定的而不是建表时临时拍脑袋。实际调研也很重要报告里提到设计动机来自观察到的校园摆摊现象这比凭空想一个电商系统要实在得多答辩时也更容易讲清楚。2.2 概念与逻辑模型局部 E-R 图先行规范化放最后概念模型设计这部分报告用了三个版本迭代先在草稿纸上画再用 PowerDesigner 建局部 E-R 图最后拼成整体 E-R 图。这个顺序是对的直接画整体图很容易在实体关系上打架。局部 E-R 图阶段它明确了买方、卖方与三类商品的“一对多”关系一个买方可以对应多本教材一本教材只能对应一个买方卖方同理。这里的本质是商品与用户之间的多对一关系在概念模型里用一对多表示转换到逻辑模型时就自然变成外键关联。逻辑模型阶段报告提到“均遵循第三范式”。这句话在课程设计报告里几乎是标配但实际有没有做到要看细节。第三范式的核心是每个非主属性完全依赖于主键且不存在传递依赖。用这里的表验证一下教材表的字段是教材编号、教材名称、出版社、价格、物主主键是教材编号出版社和价格都只依赖教材编号不依赖其他非主属性确实满足 3NF。唯一要注意的是“物主”作为外键引用卖方编号这是依赖关系的正确表达不违反范式。如果你在写自己的报告建议像这样逐表验证一遍而不是只写一句“满足第三范式”——答辩时老师很可能会追问哪几张表、怎么验证的。PowerDesigner 的用法这里补充一句概念模型转逻辑模型是自动的但转完之后一定要手动检查外键映射和数据类型。自动生成的外键列名往往是乱的比如直接叫“卖方编号_1”需要自己改成可读的名字。物理模型阶段再考虑具体的 SQL Server 类型、约束和索引。3. 物理设计与建库脚本schema 隔离、约束设置和可执行的全套建表 SQL3.1 schema 设计用架构名区分业务域是这份报告最亮眼的设计进入物理设计阶段这份报告有一个值得单独拿出来的设计决策用了users和trade两个 schema 来隔离用户信息与交易信息。这不是课程设计里常见的做法多数学生直接建一堆表堆在默认 schema 里就完事了。用 schema 划分的好处有两个一是权限管理更清晰可以按架构粒度授权这在后面的用户授权部分确实用到了二是语义上更规整users.买方和trade.教材一看就知道属于哪个业务域。建库脚本里还有一个容易被忽略的细节初始文件大小、最大限制和增长步长都显式指定了。size15, maxsize60, filegrowth5的意思是数据文件初始 15MB、最大 60MB、每次自动增长 5MB。课程设计阶段的数据量很小但把这三个参数写出来说明能区分“数据文件”和“日志文件”的差异这在答辩时是个加分项。3.2 建表 SQL约束、外键、联合主键怎么落成代码/*建立数据库指定数据文件和日志文件的路径、大小与增长策略*/ create database tradeon on ( name trade, filename d:\mssql\data\trade.mdf, size 15, maxsize 60, filegrowth 5 ) log on ( name trade_log, filename d:\mssql\log\trade.ldf, size 10, maxsize 30, filegrowth 5 ) create schema users create schema trade /*用户表主键、唯一约束、CHECK 约束一起定义*/ create table users.买方 ( 买方编号 char(6) primary key, 性别 char(2) check (性别 男 or 性别 女), 姓名 char(5) unique not null, 手机号 char(11) unique not null ) create table users.卖方 ( 卖方编号 char(6) primary key, 性别 char(2) check (性别 男 or 性别 女), 姓名 char(5) unique not null, 手机号 char(11) unique not null ) /*商品表外键引用卖方编号价格非空但允许为 0*/ create table trade.生活用品 ( 编号 char(6) primary key, 物主 char(6) foreign key references users.卖方(卖方编号), 名称 char(5), 品牌 char(10), 价格 float not null ) create table trade.教材 ( 教材编号 char(6) primary key, 物主 char(6) foreign key references users.卖方(卖方编号), 教材名称 char(10), 出版社 char(10), 价格 float not null ) /*选择表联合主键两个外键分别引用买方和商品*/ create table trade.教材选择表 ( 买方编号 char(6) foreign key references users.买方, 教材编号 char(6) foreign key references trade.教材, primary key (买方编号, 教材编号) )这段脚本有几个关键设计值得说明。char(6)作为编号类型在课程设计里完全够用但实际业务里我建议用int identity或者varchar加前缀char(6)一旦业务量上来就会成为瓶颈。float类型存价格其实不精确0.10.2 会出问题课程设计无所谓但如果你想让脚本更规范应该换成decimal(10,2)。联合主键的设计是正确的它天然防止了同一买家重复选择同一商品——这是业务规则“一人只能选一次”在数据库层面的落地。到这里顺手说一个 PowerDesigner 的实践物理模型生成脚本后不要直接拿去执行先检查列名有没有被截断、中文列名有没有变成拼音缩写。这份报告用的是中文列名PowerDesigner 的默认设置会把中文列名保留但如果你用的是英文建模再转物理模型列名转换规则一定要提前配好不然后面所有 SQL 都要重写。3.3 数据插入与字符集为什么说 char(5) 存中文姓名有隐患插入语句部分涉及一个新手必踩的坑char(5)是固定长度字符类型一个中文汉字在 SQL Server 里按一个字符算所以char(5)理论上能存 5 个汉字。但这里有个隐患——如果姓名恰好是 5 个汉字没有任何问题如果姓名只有两个字char(5)会用空格补齐到 5 位。这意味着你查询“where 姓名 张三”时可能匹配不上必须用like 张三%或者把字段改成varchar(5)。报告里插入的数据都是短姓名所以没暴露这个问题。但从工程角度姓名长度不固定应该用varchar。同样的问题也出现在教材名称、文具名称这些字段上char(10)存“数据库”三个字会补 7 个空格显示没问题但做字符串拼接或查询比较时很容易翻车。我一般看到char就条件反射地检查一下是不是真的固定长度场景——比如身份证号、手机号、编号这类才适合char名称类一律varchar。4. 视图、触发器与存储过程三类数据库对象的业务落地4.1 视图给买家做信息裁剪隐藏内部编号视图部分的设计思路很清晰买家不需要看到内部编号只需要看到名称、品牌、价格、物主。三个视图分别对应三类商品逻辑简单直接。物主字段保留是为了让买家知道是谁在卖但它暴露的是卖方编号不是手机号——手机号在触发器中才被调用。这个权限分层做得不错。/*视图隐藏编号等内部字段只暴露买家关心的信息*/ create view 生活用品列表 as select 名称, 品牌, 价格, 物主 from trade.生活用品 create view 教材列表 as select 教材名称, 出版社, 价格, 物主 from trade.教材视图的作用在课程设计报告里往往被一句话带过但实际它承担了两个任务一是简化查询二是安全隔离。这里的安全隔离体现在“买家只能看到商品不能看到卖家手机号”——手机号只在触发选择行为后再通过触发器拿到。这个设计在小型系统里够用但如果你想做得更严谨视图里连“物主”都不该暴露给普通买家只用物主编号做后续处理。4.2 触发器选择即得联系方式删除即级联清理触发器的设计是这份报告里最有业务味道的部分。报告里做了两类触发器第一类是“选择商品后打印物主电话”第二类是“卖方注销后删除其名下商品”。/*触发器买家选择生活用品后自动查询并打印物主手机号*/ create trigger dianhua on [trade].[生活用品选择表] after insert as declare wuzhu char(6), shenhuo char(6), dianhua char(11) select shenhuo 编号 from inserted select wuzhu 物主 from trade.生活用品 where shenhuo 编号 select dianhua 手机号 from users.卖方 where wuzhu 卖方编号 print dianhua这个触发器的业务逻辑是买家点击“选购”时系统自动给出卖家电话省去买家来回追问的步骤。inserted表是 SQL Server 触发器中的魔术表保存了刚插入的数据行。这里的写法有个必须知道的坑select shenhuo 编号 from inserted只取第一行如果一次性插入多行后面的行会被忽略。课程设计的数据量小感知不到这个问题但如果你以后写批量插入的触发逻辑必须用游标或基于集合的方式处理inserted中的所有行。第二类触发器likaion是级联删除的另一种实现方式/*触发器卖方注销后自动删除其名下所有商品*/ create trigger likaion on users.卖方 for delete as declare wuzhu char(6) select wuzhu 卖方编号 from deleted if wuzhu in (select 物主 from trade.生活用品) delete from trade.生活用品 where 物主 wuzhu if wuzhu in (select 物主 from trade.教材) delete from trade.教材 where 物主 wuzhu注意这里用的是for delete而不是after delete在 SQL Server 中两者等价但写for还是after会影响可读性——我习惯统一用after语义更明确。这个触发器解决的问题是不能直接删卖方因为商品表的外键还在引用他删了会报外键冲突。用触发器做级联删除是一种方案但其实更优雅的做法是在建外键时声明on delete cascade让数据库自己处理级联。课程设计的价值在于让你理解两种方案的取舍触发器能做更复杂的清理逻辑比如删商品前先删选择表记录但外键级联更简单可靠。这里只有一个层级on delete cascade就够用了。4.3 存储过程知识点覆盖很全但参数设计有个小坑存储过程部分写了两个覆盖了场景中两类典型查询按关键字模糊搜教材、按价格上限过滤生活用品。/*存储过程按关键字查询教材默认返回全部*/ create procedure trade.getext name varchar(10) % as select 教材名称, 出版社, 价格 from trade.教材 where 教材名称 like name /*存储过程按价格上限查询生活用品*/ create procedure trade.getlow price float as select 名称, 品牌, 价格 from trade.生活用品 where 价格 pricegetext的默认参数设为%这样做的好处是不传参时返回全部教材传%高数%时返回包含“高数”的所有记录。getlow用价格 price过滤业务含义是“我能接受的最高价”如果想让边界值也包含进来应改成价格 price——这个细节取决于你对“最高价”的语义定义。存储过程的价值在于把查询逻辑固化在数据库层客户端只需要传参数不需要拼 SQL。这在课程设计里是加分项因为体现了“业务逻辑下沉”的思路。关于参数化查询多说一句用like name这种方式做模糊匹配如果用户输入了%或_这样的通配符会被当成通配符解析。课程设计阶段不用考虑注入问题但如果你以后做真实系统要用escape子句处理通配符或者严格校验入参格式。5. 安全设计与备份恢复账号授权、角色管理和完整/差异/日志备份的恢复顺序5.1 用户与授权体系从登录名到角色把权限粒度控制到架构和表安全设计部分这份报告做得相当完整先建登录名login再建数据库用户user然后建角色role最后给角色授权。这个层级是 SQL Server 的标准权限模型课程设计里能写全的很少。/*创建登录名和数据库用户*/ create login 张三 with password 111 create user 开开 with default_schema trade /*创建角色并授权*/ create role kaikai create role yy grant all to kaikai with grant option grant select on trade.文具 to yy grant select on trade.生活用品 to yy grant select on trade.教材 to yy grant insert, select on trade.文具选择表 to yy as kaikai grant select on 生活用品列表 to yy grant select on 文具列表 to yy grant select on 教材列表 to yy /*将用户加入角色*/ sp_addrolemember kaikai, 开开 sp_addrolemember yy, 大牙这段脚本里最值得讲的是grant ... as kaikai这种写法它表示“以 kaikai 的身份授予 yy 权限”前提是执行授权的人拥有 kaikai 角色的with grant option权限。这是 SQL Server 权限委派机制——DBA 可以把授权能力下放给特定角色而不是所有权限都由 DBA 亲自操作。课程设计写到这里说明确实理解了授权的委派模型。密码直接明文写在脚本里这在课程设计里可以接受但真实环境必须用create login ... with password xxx配合强制密码策略或者用 Windows 身份验证。另外角色名kaikai和yy起得太随意可读性差建议用业务语义命名比如buyer_role、seller_role。5.2 备份恢复策略三种备份类型的配合方式和完整恢复序列备份恢复这部分是这份报告最扎实的章节之一。它设计了一个完整的备份链路创建数据库后先做全量备份插入数据后做差异备份再操作一段时间后做日志备份。出现故障时的恢复顺序是尾日志备份 → 全量恢复 → 差异恢复 → 日志恢复 → 尾日志恢复。/*数据库创建后设置为完全恢复模式*/ alter database trade set recovery full /*完整备份*/ backup database trade to disk D:\备份\full.bak /*插入数据后做差异备份*/ backup database trade to disk D:\备份\diffl.bak with differential /*再插入数据后做日志备份*/ backup log trade to disk D:\备份\logl.bak /*故障恢复尾日志备份后按顺序恢复*/ backup log trade to disk D:\备份\log2.bak restore database trade from disk D:\备份\full.bak with norecovery restore database trade from disk D:\备份\diffl.bak with norecovery restore log trade from disk D:\备份\log1.bak with norecoverywith norecovery是关键参数它表示恢复完当前备份后数据库保持“正在恢复”状态继续等待下一个备份文件的恢复直到最后一步去掉norecovery让数据库可用。如果中间某一步用了with recovery后续备份就无法继续恢复了——这是恢复流程中最容易翻车的点。这套备份策略的好处是恢复时间可控全量备份耗时较长但恢复完整差异备份只记录上次全量以来的变化恢复速度快日志备份记录所有事务能把数据损失控制在最后一次日志备份的时间点内。理解这个“全量差异日志”的组合比背概念有用得多——因为恢复顺序错了数据就丢了这种错误在面试题里出现频率很高。5.3 数据库恢复模式为什么先设 FULL 再备份报告特意强调“刚创建数据库时设置恢复模式为完全恢复”这背后的原因是SQL Server 默认的恢复模式是简单模式simple简单模式下只能做完整备份和差异备份不能做日志备份因为日志会被自动截断。如果一开始就做全量备份然后再alter database set recovery full之前的备份仍然有效但更规范的做法是先设恢复模式再开始备份链路。另一个容易被忽略的细节日志备份会截断日志文件但不做日志备份的话日志文件会持续增长直到磁盘写满。所以“日志备份”不仅是为了恢复数据也是日志文件管理的手段。这份报告里有备份脚本但没有提到日志文件增长监控——如果你在真实环境里做这套建议加一条定期检查log_reuse_wait_desc的习惯防止日志文件膨胀。6. 事务并发控制与避坑XACT_ABORT、锁提示和四个实战翻车点6.1 事务回滚与并发读写XACT_ABORT 的正确用法和锁提示的取舍事务控制部分报告用了一段TRY-CATCH结构来演示事务回滚。这个模式本身是对的但有一个重要的前置条件必须设置SET XACT_ABORT ON。如果不加这句某些运行时错误比如违反约束只回滚当前语句而不会回滚整个事务结果就是事务处于“部分提交”状态这是最难排查的数据库问题之一。/*事务回滚示例设置 XACT_ABORT 后任何错误都会回滚整个事务*/ set xact_abort on begin try begin transaction insert into trade.文具 values (1, 2, 钢笔, 5) insert into trade.文具 values (1, 2, 铅笔, 6) commit transaction end try begin catch if (xact_state()) -1 begin print 事务不能提交撤销事务 rollback transaction end end catch这段代码还演示了xact_state()的用法它返回当前事务状态-1表示事务已被标记为“不可提交”需要 rollback0表示没有活动事务1表示事务可提交。注意第二行插入语句故意用了和第一行相同的主键1会触发主键冲突从而让整体事务回滚——这其实是个刻意构造的翻车演示用来证明回滚机制确实生效。从教学角度看这个示例比单纯讲事务概念直观得多。并发控制部分写了避免脏读和不可重复读的示例分别用了UPDLOCK更新锁防止其他事务同时更新同一行和TABLOCK HOLDLOCK表级锁并持有到事务结束。这两个锁提示的方向是对的UPDLOCK解决的是更新场景下的死锁和脏写问题TABLOCK HOLDLOCK解决的是同一事务内两次查询结果不一致的问题。但实际业务中这两个锁提示的粒度都偏粗——TABLOCK锁整张表并发量一大就会出现大量阻塞。理解锁的粒度差异比记住特定锁提示更重要SQL Server 默认的锁粒度是行级只有查询条件没走索引时才会升级到表锁。6.2 避坑记录这份报告暴露的四个典型问题坑一建库语句的库名不一致建库脚本写的是create database tradeon但文件逻辑名写的是name trade数据库名是tradeon而逻辑文件名是trade执行后use tradeon才能进库。如果你在 SQL Server 里手动敲这个脚本很容易被搞晕。原因在于作者没有统一命名规范。解决方法是建库时让逻辑文件名与数据库名保持一致或者干脆用默认命名。坑二char(n)存中文名称导致查询匹配失败前面提到char会补空格而中文姓名和商品名长度不固定。排查方法是看到查询条件where 教材名称 数据库查不出来时先检查字段类型是不是char如果是就改用varchar。或者用rtrim()去掉尾部空格。这个问题的诡异之处在于用 SSMS 看数据时显示正常因为可视化界面把空格渲染掉了但直接在查询窗口对比就不匹配。这种“看着一样但查不到”的情况最容易浪费半小时。坑三触发器只用select from inserted取单行第一个触发器dianhua用select shenhuo 编号 from inserted这只适用于单行插入。如果用户一次插入多条选择记录只有第一条能触发电话打印。原因是对触发器魔术表的理解不完整——inserted表可以包含多行。解决方法是用inner join或游标逐行处理。课程设计数据量小看不出问题但面试时问到“多行插入怎么处理触发器”就是这个坑。坑四事务示例中SET 教材编号 WHERE 物主 是残缺语句报告里的不可重复读示例中SET 教材编号 WHERE 物主 没有赋值也没有指定教材编号的具体值这是复制或排版时丢掉了内容。这种残缺代码放进报告里答辩时被老师看到会很扣分。但这个坑也提醒了所有写课程设计报告的人交稿前一定要把报告里的每段代码过一遍最好在 SQL Server 里实际执行一次把执行结果截图放进文档。代码能跑通和看起来能跑通是两回事不少老师只看代码就会直接指出问题。6.3 技巧从这份报告里提炼可复用的课程设计模板这份报告虽然有些瑕疵但整体结构是很好的课程设计模板照着走一遍比从零开始摸索高效得多。我的建议是把它的四层模型设计流程抄下来但把业务场景换成你自己的题目——比如二手书回收、实验室设备借用、社团活动报名之类的。表结构设计时保留schema隔离、联合主键、外键约束这三个要点。数据库对象必须包含两个触发器和一个存储过程事务部分至少演示一个回滚场景。备份恢复章节不需要真的把备份文件恢复到另一个实例但脚本要能解释清楚恢复顺序。安全设计部分角色授权至少要写两个角色还有“某个用户既然能查表又能以另一个身份授权”这种权限委派的细节。做课程设计时最容易忽略的是“把报告当代码仓库用”的价值你辛辛苦苦调通的每个触发器、每个存储过程都值得在文档里记录当时的报错信息和解决过程。这份报告的作者在个人总结里写“自己抠脑袋解决的 bug 印象极为深刻”这其实点出了课程设计真正的收获——不在于做出完美的数据库而在于踩过坑、查过文档、修过 bug。从那以后我每写一份数据库相关的文档都会强制把排错过程单独写一节不只是为了凑篇幅是因为下次再遇到同类问题翻自己的笔记比重新搜索快得多。这份二手交易系统报告同样是这个思路的产物——它不完美但每一步都真实可查照着走一遍你会比看十篇“完美”的理论文章收获更大。希望帮到你。本文还有配套的精品资源点击获取