☰
校园二手交易系统数据库课程设计:从PowerDesigner建模到SQL Server完整实现
2026/10/12 1:01:02 网站建设 项目流程

简介:这份数据库课程设计文档面向高校数据库系统及应用课程的选课学生与指导教师,以「校园二手交易系统」为完整案例,解决从需求调研到数据库落地缺乏规范范本的问题。资源包共1个doc文件,约1.72MB,内容为一份结构完整的实验报告,涵盖需求说明书、概念E-R模型、逻辑模型规范化、物理模型设计,以及视图、触发器、存储过程、安全与恢复方案、事务设计等数据库对象,并附系统实现与个人工作报告。报告还记录了使用PowerDesigner建模、SQL Server与Visual Studio开发工具的具体过程,以及小组分工、自评与总结,可作为课程设计报告的参考模板。目前已有1572人学习下载,适合需要完成数据库课程设计、学习CASE工具建模流程或准备校园二手交易系统方案的学生参考借鉴。

1. 校园二手交易系统数据库:一份能直接跑通的课程设计文档

如果你正在搜「数据库课程设计」或者「校园二手交易系统」,大概率是两种情况:要么课程设计选题还没定,要么已经定了但不知道一份完整的报告该写到什么颗粒度。这份文档给的是一个完整的答案——从需求分析、PowerDesigner 概念模型、逻辑模型、物理模型,一直到 SQL Server 建库脚本、视图、触发器、存储过程、用户授权和事务并发控制,全流程都有。它不是那种只给 ER 图就结束的模板,而是把物理设计阶段该有的约束、安全、备份恢复方案都落到了具体 T-SQL 语句上。适合数据库原理刚学完、需要一份可参照的完整设计文档来动手复现的同学,也适合想看看别人怎么把「教材交易、文具交易、生活用品交易」三个模块拆成独立表结构的人。

2. 从需求到 E-R 图:PowerDesigner 建模的四个阶段怎么落地

2.1 需求分析:三个交易模块怎么拆

这份设计的业务场景很具体:校园里学生之间买卖二手教材、文具和生活用品。系统里有两类角色——买家和卖家,但实际是同一个人可以同时是买家和卖家。需求分析阶段要做的第一件事,是把「用户」和「交易物品」之间的关系理清楚。

从文档里的表结构反推,需求被拆成了三个独立的交易模块:教材交易、文具交易、生活用品交易。每个模块都有「物品表」和「选择表」两张核心表。物品表存的是卖家发布的商品信息,选择表存的是买家对商品的选购记录。这种拆法有一个明显的好处:三类商品的属性差异较大,教材有出版社、文具没有品牌但有名称、生活用品有品牌,强行合并成一张「商品表」会导致大量空字段。分开建表虽然增加了表数量,但每张表的字段都干净。

常见做法是先用文字把需求写成条目,再画局部 E-R 图。文档里提到「在草稿纸上涂涂改改了三个版本」,说明需求分析阶段反复调整是正常的。我一般会先把实体和联系列成一张清单,确认每个实体的主键和关键属性,再进 PowerDesigner 画图。

2.2 概念模型:局部 E-R 图到整体 E-R 图

PowerDesigner 的概念模型阶段,核心工作是画 E-R 图。这份文档的做法是先画局部 E-R 图,再合并成整体 E-R 图。局部 E-R 图按业务模块划分:买家与教材、卖家与教材、买家与文具、卖家与文具、买家与生活用品、卖家与生活用品,每组都是「一对多」关系。

这里有一个容易翻车的地方:买家和物品之间到底是「一对多」还是「多对多」?文档里的处理方式是——买家与物品之间通过「选择表」建立联系,选择表的主键是「买家编号 + 物品编号」的复合主键,这样就把多对多关系拆成了两个一对多。这是标准做法,但新手经常直接在 E-R 图上画一条多对多连线就完事,到了逻辑模型阶段才发现没法直接建表。

在 PowerDesigner 里操作时,先建 Entity(实体),再建 Relationship(联系),注意设置 Cardinality(基数)。局部 E-R 图完成后,用「Merge Models」功能合并成整体 E-R 图。合并时重点检查:有没有同名实体、有没有冲突的联系定义、主键是否一致。

2.3 逻辑模型:关系规范化到 3NF

概念模型转逻辑模型,PowerDesigner 可以一键生成,但生成之后必须手动检查。文档里明确写了「以上所有关系均遵循第三范式」。3NF 的要求是:每个非主属性都直接依赖于主键,不存在传递依赖。

以教材表为例:教材编号是主键,教材名称、出版社、价格、物主都直接依赖教材编号。物主是外键,指向卖方表的卖方编号。这里没有传递依赖——出版社不依赖于教材名称,价格也不依赖于出版社。符合 3NF。

但有一个细节值得注意:文档里买方表和卖方表是分开建的,字段几乎一样(编号、姓名、性别、手机号)。从规范化角度看,这其实是冗余的——同一个人既是买家又是卖家,信息存了两遍。但文档里的处理方式是两张表都保留,插入数据时也确实把同样的 10 个人同时插入了买方表和卖方表。这种设计在实际系统里会增加数据不一致的风险,但在课程设计场景下,它让「买家」和「卖家」的角色边界更清晰,授权时也更好控制。如果你要复现,可以保留这个设计,但心里要清楚它的取舍。

2.4 物理模型:从模型到建库脚本

物理模型阶段是这份文档最实在的部分。PowerDesigner 生成物理模型后,可以直接导出 SQL 脚本,但文档里的脚本明显是手写调整过的。建库语句指定了数据文件和日志文件的具体路径、初始大小、最大大小和增长量:

create database trade 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)

这段代码里几个参数需要根据实际环境调整:filename的路径必须是你机器上真实存在的目录,否则建库直接报错;size单位是 MB,15MB 对课程设计够用;filegrowth=5表示每次增长 5MB,如果插入数据量大可以改成百分比。我一般会把maxsize设得比预期数据量大一倍,避免后期频繁手动扩文件。

建完库之后创建了两个 schema:users和trade。这是 SQL Server 2005/2008 才支持的特性,把用户相关表和交易相关表分到不同 schema 下,权限管理更清晰。如果你的环境是 SQL Server 2000,这一步会直接报错,需要改成用不同数据库用户来隔离。

3. 物理设计核心:约束、视图、触发器与存储过程的 T-SQL 实现

3.1 表约束:主键、外键、唯一与 CHECK

建表语句里把完整性约束写得比较全。以买方表为例:

create table users.买方( 买方编号 char(6) primary key, 性别 char(2) check(性别='男'or 性别='女'), 姓名 char(5) unique not null, 手机号 char(11) unique not null )

这里有三类约束:primary key保证买方编号唯一且非空;check限制性别只能取「男」或「女」;unique保证姓名和手机号不重复。注意char(5)存姓名,如果名字超过 5 个字符会截断或报错,实际用varchar(20)更稳妥。手机号用char(11)是合理的,国内手机号固定 11 位。

外键约束在物品表里体现:

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) foreign key references users.买方, 编号 char(6) foreign key references trade.生活用品, primary key(买方编号,编号) )

两个字段联合做主键,保证同一个买家不会重复选择同一件物品。同时两个字段各自是外键,保证引用的买家和物品都真实存在。

3.2 视图:给用户看的「干净」数据

文档里创建了三个视图,目的是把编号之类的内部字段藏起来,只暴露用户关心的信息:

create view 生活用品列表 as select 名称,品牌,价格,物主 from trade.生活用品

这个视图只返回四个字段,买方看到的就是商品名称、品牌、价格和物主编号。物主编号虽然还是编号,但至少比直接暴露整张表要好。实际项目中,物主编号通常也会替换成物主姓名,但文档里没做这个关联,算是一个可以改进的点。

视图的另一个作用是简化授权。后面授权部分可以看到,对视图授select权限比对整张表授权限更安全,因为用户只能看到视图定义的列。

3.3 触发器:自动带出电话与级联清理

文档里做了两类触发器,第一类是在买方选择物品后自动打印物主电话:

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临时表拿到物品编号,查物品表拿到物主编号,再查卖方表拿到手机号,最后print出来。这里用print只是演示,实际系统里应该把电话写到某个日志表或者返回给应用层。注意inserted表在批量插入时可能有多行,这个触发器只取了第一行,批量插入会漏数据。常见做法是用select @dianhua = 手机号 from ...配合top 1或者改成游标处理,但课程设计场景下单条插入够用。

第二类触发器是卖方删除时级联清理其名下物品:

create trigger likai 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 if @wuzhu in (select 物主 from trade.教材) delete from trade.教材 where 物主=@wuzhu

这个触发器实现了「卖方离开后清除其下属相关物品」。但有一个隐患:如果物品已经被买家选择,选择表里有外键引用,直接删物品会触发外键冲突。文档里没有处理这个情况,实际运行时如果先插了选择表再删卖方,会报错。解决方式是在删物品之前先删选择表里对应的记录,或者把外键改成on delete cascade。

3.4 存储过程:关键字模糊查询与价格筛选

两个存储过程分别解决两个查询需求:

create procedure trade.getext @name varchar(10)='%' as select 教材名称,出版社,价格 from trade.教材 where 教材名称 like @name

这个存储过程接收一个教材名称关键字,默认值是'%',表示不传参数时返回全部教材。调用方式:exec trade.getext '高数'会返回所有名称包含「高数」的教材。参数varchar(10)对教材名称来说偏短,实际可以放宽到varchar(50)。

create procedure trade.getlow @price float as select 名称,品牌,价格 from trade.生活用品 where 价格<@price

这个存储过程接收一个最高价,返回所有低于该价格的生活用品。调用方式:exec trade.getlow 10返回价格低于 10 元的生活用品。注意float类型做价格比较会有精度问题,实际项目里用decimal(10,2)更合适。

3.5 安全设计与授权

授权部分展示了 SQL Server 的角色管理机制。先创建登录名和数据库用户,再创建角色,把权限授予角色,最后把用户加入角色:

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 sp_addrolemember 'kaikai','开开' sp_addrolemember 'yy','大牙'

kaikai角色拥有全部权限并且可以转授,yy角色只有查询和插入选择表的权限。as kaikai表示以 kaikai 角色的身份授予权限,这是 SQL Server 的权限链机制。最后用sp_addrolemember把具体用户加入角色。这套流程在课程设计里算完整,但实际项目中还需要考虑密码策略、登录审计等。

4. 备份恢复与事务并发:课程设计里最容易忽略的实操环节

4.1 备份策略:完整、差异、日志三层

文档里的备份方案分三步:建库后设恢复模式为full,创建表后做完整备份,插入数据后做差异备份,再插入数据后做日志备份。

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'

恢复时的顺序是:先做尾日志备份,再依次恢复完整备份、差异备份、日志备份,每一步都要加with norecovery,最后一步才恢复尾日志。

backup log trade to disk='D:\备份\log2.bak' restore database trade from disk='D:\备份\full.bak' with norecovery restore database trade from disk='D:\备份\diff1.bak' with norecovery restore log trade from disk='D:\备份\log1.bak' with norecovery

这里有一个血泪经验:with norecovery表示恢复后数据库还处于「正在还原」状态,不能再执行其他操作,必须等所有备份文件都恢复完才能让数据库上线。如果中间漏了一个文件或者顺序错了,数据库会一直卡在还原状态,只能从头再来。我一般会在恢复前把备份文件按时间顺序列好,恢复时逐个核对。

4.2 事务与并发控制

文档里给了三个事务示例。第一个是try...catch配合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

两条插入语句用的是同一个主键'1',第二条会报主键冲突。xact_abort on让运行时错误自动终止事务,catch块里判断xact_state()=-1表示事务不可提交,执行回滚。这个模式在实际项目里很常用,比手动判断错误码要可靠。

第二个示例演示脏读:

begin transaction update trade.教材 with(updlock) set 教材编号= where 物主='' rollback transaction

with(updlock)在读取时加更新锁,防止其他事务读到未提交的数据。但这条语句本身有问题:set 教材编号=后面没有值,语法不完整。文档里可能是省略了具体值,实际执行会报错。

第三个示例演示不可重复读:

begin transaction select * from trade.文具 with(tablock holdlock) where 文具编号='' and 文具名称=''

tablock holdlock在表级别加共享锁并保持到事务结束,防止其他事务在两次读取之间修改数据。但where条件里编号和名称都是空字符串,实际查不到任何数据,这个示例更多是语法演示。

5. 复现这份课程设计的几个关键技巧

5.1 环境搭建的版本坑

文档指定的环境是 SQL Server 2005/2008 + Visual Studio.net + PowerDesigner。如果你现在用 SQL Server 2019 或 2022 复现,大部分语法兼容,但有几个点要注意:sp_addrolemember在新版本里被标记为弃用,建议改用alter role ... add member;create login的密码策略在新版本里默认开启,如果密码太简单会报错,需要先关掉密码策略或者用复杂密码。

PowerDesigner 的版本差异更大。文档里没有写具体版本号,但 16.5 和 16.7 的界面和导出选项有区别。如果你只是要生成建库脚本,用 PowerDesigner 的「Generate Database」功能,选择 SQL Server 2008 作为目标数据库,导出的脚本基本可以直接用。如果导出的脚本里有go语句,在 SQL Server Management Studio 里执行时需要确保go是单独一行。

5.2 数据插入顺序与外键依赖

文档里的插入顺序是:先插卖方和买方,再插生活用品、文具、教材,最后插选择表。这个顺序不能变,因为物品表的外键指向卖方表,选择表的外键指向买方表和物品表。如果你先插选择表,会直接报外键冲突。

另外注意insert trade.生活用品 values('1','1','牙膏','云南白药',5)这条语句,第一个'1'是物品编号,第二个'1'是物主编号,对应卖方表里卖方编号为'1'的记录。如果卖方表里没有'1'这条记录,插入会失败。文档里卖方表插入了 10 条记录,编号从'1'到'10',所以物品表的物主编号都在这个范围内。

5.3 触发器调试:print 语句看不到怎么办

文档里的触发器用print输出电话,但在 SQL Server Management Studio 里执行insert语句时,print的输出在「消息」标签页里,不在「结果」标签页。如果你只盯着结果集看,会以为触发器没生效。另外print有长度限制,超过 4000 字符会被截断,手机号只有 11 位,不存在这个问题。

如果触发器报错,先检查inserted表里有没有数据。after insert触发器在插入语句执行后触发,inserted表里是刚插入的行。如果插入被外键约束阻止,触发器根本不会执行。

5.4 备份路径必须提前创建

backup database trade to disk='D:\备份\full.bak'这条语句要求D:\备份\目录必须存在。如果目录不存在,SQL Server 不会自动创建,直接报错「无法打开备份设备」。我一般会在执行备份脚本之前先手动建好目录,或者在脚本里加一段xp_cmdshell来创建目录,但xp_cmdshell默认是关闭的,需要先启用。

5.5 授权语句的执行顺序

授权部分的语句顺序也有讲究。必须先create login,再create user,然后create role,接着grant权限给角色,最后sp_addrolemember把用户加入角色。如果先grant再create role,会报角色不存在。另外grant all to kaikai with grant option里的all表示所有权限,但在实际项目中不建议授all,应该按最小权限原则逐项授予。

5.6 事务示例的语法修正

文档里的事务示例有几处语法不完整,复现时需要补全。比如update trade.教材 with(updlock) set 教材编号= where 物主=''缺少赋值,应该写成set 教材编号='新值' where 物主='某值'。select * from trade.文具 with(tablock holdlock) where 文具编号='' and 文具名称=''里的空字符串条件查不到数据,可以改成具体的编号和名称来观察锁的行为。

复现这份文档最大的价值不在于照抄代码,而在于理解每个数据库对象为什么这样设计。比如触发器为什么用after insert而不是instead of insert,存储过程的默认参数'%'解决了什么问题,备份恢复为什么要分三种类型。把这些想清楚了,换个题目做课程设计也能自己推导出方案。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询