ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

数据库课设实战:机房管理系统从建库到PowerBuilder界面完整实现

数据库课设实战:机房管理系统从建库到PowerBuilder界面完整实现 简介这份资源是广东工业大学数据库课程设计的完整Word版报告面向高校数据库课程学习者与课程设计参与者聚焦机房管理系统的设计与实现。报告以SQL Server 2005与PowerBuilder为开发环境系统梳理了从需求分析到应用程序调试的全流程适合需要完成同类课程设计或学习数据库应用开发的学生参考。压缩包内仅含1个doc文档约1.27MB内容结构完整涵盖系统需求分析与功能设计、总体设计、数据库设计、应用程序调试和界面设计等章节。其中数据库设计部分详细给出了E-R图、逻辑模型及T-SQL建表过程需求分析部分则围绕设备采购、登记、借用归还、维修报废及机房上机安排等业务展开并配有总体功能模块图与菜单设计说明。目前已有185人学习下载读者可借此获得一份可直接参照的课程设计范例理解机房管理系统的模块划分、数据建模思路与调试方法为撰写报告和搭建系统提供实用参考。1. 机房管理系统课设从一份 Word 文档到能跑起来的数据库应用如果你手里正躺着一份《广东工业大学数据库课程设计机房管理系统设计.doc》打开一看全是需求描述和 E-R 图却不知道从哪一行代码下手这篇笔记就是写给你的。机房管理系统这个题目在数据库课设里出现频率极高核心无非是机房、机器、学生、上机记录、计费这几张表但真正动手时你会发现SQL Server 装哪个版本、PowerBuilder 怎么连库、T-SQL 触发器写在哪一层、增删改查的界面怎么跟存储过程对上每一步都有坑。我当年做这个课设时光是把 PowerBuilder 12.5 连上 SQL Server 就折腾了一下午后来带学弟做又踩了一遍。这篇把从建库、建表、写 T-SQL、到用 PowerBuilder 做界面、再到调试排错的完整路径拆开讲新手能照着复现熟手能对照检查自己的参数和边界。适合正在做数据库课设、需要交一份能演示的系统、或者想用一个小项目把 SQL Server 和 PowerBuilder 串起来的人。2. 先把需求翻译成表机房管理系统的库表设计与字段取舍2.1 从 Word 需求里抽出五张核心表课设文档里的需求通常写得很散什么“学生可以刷卡上机”“管理员可以查询历史记录”“机器故障要登记”这些句子要翻译成表结构。我一般先画一张实体清单学生、机房、机器、上机记录、管理员。这五个实体对应五张表其中上机记录是核心事实表其他都是维度表。学生表存学号、姓名、班级、余额机房表存机房编号、名称、位置、开放时间机器表存机器编号、所属机房、状态上机记录表存记录号、学号、机器编号、开始时间、结束时间、费用管理员表存工号、姓名、密码。这里有个取舍余额到底放学生表还是单独做账户流水表。课设级别我建议直接放学生表因为计费逻辑简单扣费就是一条 UPDATE。如果要做充值记录再加一张充值表但别一上来就搞复杂先把主流程跑通。-- 建库字符集用默认即可课设数据量小 CREATE DATABASE MachineRoomDB; GO USE MachineRoomDB; GO -- 学生表余额用 DECIMAL别用 FLOAT钱不能用浮点 CREATE TABLE Student ( StuID VARCHAR(12) PRIMARY KEY, -- 学号 StuName NVARCHAR(20) NOT NULL, ClassName NVARCHAR(30), Balance DECIMAL(10,2) DEFAULT 0.00, Status TINYINT DEFAULT 1 -- 1正常 0冻结 ); -- 机房表 CREATE TABLE Room ( RoomID VARCHAR(6) PRIMARY KEY, RoomName NVARCHAR(30), Location NVARCHAR(50), OpenTime TIME, CloseTime TIME ); -- 机器表状态 1空闲 2使用中 3故障 CREATE TABLE Machine ( MacID VARCHAR(10) PRIMARY KEY, RoomID VARCHAR(6) FOREIGN KEY REFERENCES Room(RoomID), MacStatus TINYINT DEFAULT 1 ); -- 上机记录表结束时间允许为空表示还在上机 CREATE TABLE LoginRecord ( RecordID INT IDENTITY(1,1) PRIMARY KEY, StuID VARCHAR(12) FOREIGN KEY REFERENCES Student(StuID), MacID VARCHAR(10) FOREIGN KEY REFERENCES Machine(MacID), StartTime DATETIME DEFAULT GETDATE(), EndTime DATETIME NULL, Fee DECIMAL(10,2) DEFAULT 0.00 );字段类型的选择有讲究。学号用 VARCHAR 而不是 INT因为学号可能有前导零用 INT 会丢零。金额用 DECIMAL(10,2)FLOAT 做减法会出现 0.30000000000000004 这种结果计费场景下这是血泪教训。时间用 DATETIME课设不需要 DATETIME2 的精度。状态字段用 TINYINT 加注释比用字符串省空间也快。2.2 主键、外键和索引怎么定主键的选择直接影响后面写 T-SQL 的手感。学生表用学号做主键因为学号天然唯一且业务上常用它查询。机器表用机器编号做主键编号规则一般是“机房号序号”比如 A101-01。上机记录表用 IDENTITY 自增列做主键因为记录本身没有业务主键自增列最省事。外键要不要加课设里我建议加因为能帮你自动挡住脏数据。比如往 LoginRecord 插一条 StuID 不存在的记录数据库直接报错省得你在应用层写校验。但外键也会带来删除顺序问题删学生之前得先删他的上机记录否则报错。这个在写删除功能时要注意。索引方面上机记录表按学号和时间查询最频繁建一个复合索引-- 按学号查历史记录是高频操作 CREATE INDEX IX_LoginRecord_StuID_StartTime ON LoginRecord(StuID, StartTime DESC); -- 查某台机器的使用记录 CREATE INDEX IX_LoginRecord_MacID ON LoginRecord(MacID);索引不是越多越好每多一个索引插入和更新就多一份维护成本。课设数据量小这两个索引足够。如果你用 SQL Server 2019 或 2022可以用图形化界面看执行计划确认查询走了索引而不是全表扫描。2.3 用 T-SQL 写计费存储过程计费逻辑是机房管理系统的核心。规则通常是按小时计费不足一小时按一小时算每小时单价假设 2 元。这个逻辑写在存储过程里应用层调用即可避免把业务规则散落在 PowerBuilder 代码里。-- 下机结算传入记录号计算费用并更新 CREATE PROCEDURE sp_CheckOut RecordID INT AS BEGIN SET NOCOUNT ON; DECLARE StartTime DATETIME, EndTime DATETIME; DECLARE Minutes INT, Hours INT, Fee DECIMAL(10,2); DECLARE StuID VARCHAR(12); SELECT StartTime StartTime, StuID StuID FROM LoginRecord WHERE RecordID RecordID AND EndTime IS NULL; IF StartTime IS NULL BEGIN RAISERROR(记录不存在或已结算, 16, 1); RETURN; END SET EndTime GETDATE(); SET Minutes DATEDIFF(MINUTE, StartTime, EndTime); -- 不足一小时按一小时向上取整 SET Hours CEILING(Minutes / 60.0); IF Hours 0 SET Hours 1; SET Fee Hours * 2.00; UPDATE LoginRecord SET EndTime EndTime, Fee Fee WHERE RecordID RecordID; UPDATE Student SET Balance Balance - Fee WHERE StuID StuID; UPDATE Machine SET MacStatus 1 WHERE MacID (SELECT MacID FROM LoginRecord WHERE RecordID RecordID); END这段存储过程有几个关键点。CEILING 做向上取整保证不足一小时也收一小时的钱。判断 StartTime IS NULL 是为了防止重复结算如果记录已经结算过EndTime 不为空查询就查不到直接报错返回。更新机器状态放在最后把机器置为空闲。参数 RecordID 是唯一入参应用层只需要传记录号。调用方式EXEC sp_CheckOut RecordID 1;如果你在 PowerBuilder 里调用用 SQLCA 的 EXECUTE 语句注意参数绑定方式后面会讲。3. PowerBuilder 连 SQL Server从驱动配置到 DataWindow 绑定3.1 配置 ODBC 数据源与数据库连接PowerBuilder 连 SQL Server 最常见的方式是走 ODBC。先在 Windows 的 ODBC 数据源管理器里建一个系统 DSN指向你的 SQL Server 实例和 MachineRoomDB 库。SQL Server 2012 及以上版本用 “SQL Server Native Client” 或 “ODBC Driver 17 for SQL Server”。建 DSN 时注意服务器地址填localhost或.\SQLEXPRESS取决于你的实例名登录方式用 SQL Server 身份验证填 sa 账号和密码别用 Windows 验证因为 PowerBuilder 的 ODBC 连接对 Windows 验证支持有时会出玄学问题。建好 DSN 后在 PowerBuilder 的 Application 对象的 Open 事件里写连接代码// PowerScript 代码不是命令行 SQLCA.DBMS ODBC SQLCA.AutoCommit False SQLCA.DBParm ConnectStringDSNMachineRoomDB;UIDsa;PWDyourpassword CONNECT USING SQLCA; IF SQLCA.SQLCode 0 THEN MessageBox(连接失败, SQLCA.SQLErrText) HALT CLOSE END IFDBMS 填 “ODBC”DBParm 里的 ConnectString 直接写 DSN 名称和账号密码。AutoCommit 设为 False这样你可以手动控制事务提交计费这种操作必须在一个事务里完成。SQLCode 返回 0 表示成功非 0 就弹错误信息并退出。这里有个坑如果你的 sa 密码里有分号ConnectString 会解析错误得用花括号包起来或者换密码。3.2 用 DataWindow 做增删改查界面PowerBuilder 的 DataWindow 是它的核心武器做增删改查比手写 SQL 快得多。新建一个 DataWindow数据源选 SQL Select把 Student 表的字段全选上。然后设置更新属性在 DataWindow 画板的 Rows 菜单里选 Update PropertiesTable to Update 选 StudentUnique Key Column 选 StuID勾上 Updateable Columns 里需要更新的列。界面上放一个 DataWindow 控件命名为 dw_student再放几个按钮新增、删除、保存。新增按钮的 Clicked 事件// 在 DataWindow 末尾插入一条空记录 dw_student.InsertRow(0) dw_student.ScrollToRow(dw_student.RowCount())删除按钮// 删除当前行注意要用户确认 IF MessageBox(确认, 确定删除该学生, Question!, YesNo!) 1 THEN dw_student.DeleteRow(0) END IF保存按钮// 提交所有修改到数据库 IF dw_student.Update() 1 THEN COMMIT USING SQLCA; MessageBox(成功, 保存成功) ELSE ROLLBACK USING SQLCA; MessageBox(失败, 保存失败 SQLCA.SQLErrText) END IFUpdate() 返回 1 表示成功返回 -1 表示失败。成功就 COMMIT失败就 ROLLBACK这是标准的事务处理模式。注意 DeleteRow(0) 里的 0 表示当前行不是第一行这个参数容易搞混。3.3 调用存储过程完成上机与下机上机操作需要同时做三件事往 LoginRecord 插一条记录、把 Machine 状态改成使用中、检查学生余额是否足够。这三步必须在一个事务里否则会出现机器被占用但记录没插入的脏状态。// 上机按钮先检查余额再插记录再改机器状态 DECLARE StuID VARCHAR(12), MacID VARCHAR(10) StuID sle_stuid.Text MacID sle_macid.Text // 检查余额 SELECT Balance INTO :dec_balance FROM Student WHERE StuID :StuID; IF dec_balance 2.00 THEN MessageBox(提示, 余额不足请先充值) RETURN END IF // 插入上机记录 INSERT INTO LoginRecord(StuID, MacID, StartTime) VALUES (:StuID, :MacID, GETDATE()); // 更新机器状态 UPDATE Machine SET MacStatus 2 WHERE MacID :MacID; COMMIT USING SQLCA;PowerScript 里用冒号加变量名做绑定变量比如:StuID。这种写法能防止 SQL 注入也比字符串拼接安全。下机操作直接调用前面写的存储过程// 下机按钮调用 sp_CheckOut DECLARE proc_checkout PROCEDURE FOR sp_CheckOut RecordID :int_recordid; EXECUTE proc_checkout; FETCH proc_checkout INTO :int_dummy; CLOSE proc_checkout; COMMIT USING SQLCA;这里用 DECLARE PROCEDURE 声明存储过程调用EXECUTE 执行FETCH 取返回值。如果存储过程没有返回值FETCH 可以省略但 CLOSE 必须调用否则游标不释放。4. 课设里最容易翻车的五个地方避坑与排查4.1 现象PowerBuilder 连不上数据库报 “Cannot connect to database”原因通常有三个DSN 没建对、账号密码错、SQL Server 的 TCP/IP 协议没启用。先检查 ODBC 数据源管理器里测试连接是否通过如果那里都连不上PowerBuilder 肯定连不上。然后打开 SQL Server 配置管理器确认 SQL Server 网络配置里的 TCP/IP 已启用端口默认 1433。如果用的是 SQL Server Express实例名是 SQLEXPRESS端口可能是动态的需要在 TCP/IP 属性里把 IPAll 的 TCP 动态端口清空TCP 端口固定为 1433。解决步骤先在 ODBC 里测试通过再在 PowerBuilder 里连。如果 ODBC 测试通过但 PowerBuilder 报错检查 DBParm 字符串里的 DSN 名称是否和 ODBC 里完全一致大小写敏感。4.2 现象DataWindow 更新时报 “Row changed between retrieve and update”这是并发冲突的典型报错。原因是你检索数据后别人改了同一行你再更新时数据库发现行版本变了拒绝更新。课设里单人操作一般不会遇到但如果你开了两个 PowerBuilder 实例同时改同一条记录就会触发。解决办法在 DataWindow 的 Update Properties 里把 “Where Clause for Update/Delete” 从默认的 “Key Columns” 改成 “Key and Updateable Columns”或者直接改成 “Key and Modified Columns”。前者更严格后者更宽松。课设里用 “Key Columns” 就够了因为只有你一个人在操作。4.3 现象计费结果不对明明上了 30 分钟却扣了 2 小时的钱原因在 CEILING 函数的参数类型。CEILING(Minutes / 60.0)里如果 Minutes 是 INT60.0 是 DECIMAL除法结果会自动转成 DECIMALCEILING 后是 1。但如果写成CEILING(Minutes / 60)两个 INT 相除会做整数除法30/600CEILING(0)0然后你判断 Hours0 时设为 1结果还是 1 小时。问题出在如果上了 61 分钟61/60 整数除法得 1CEILING(1)1但实际应该收 2 小时。所以必须用 60.0 强制浮点除法。排查方法在存储过程里加 PRINT 语句输出 Minutes 和 Hours在 SQL Server Management Studio 里直接 EXEC 看消息窗口。4.4 现象删除学生时报外键冲突原因是你想删的学生还有上机记录没删。外键约束阻止了删除操作。解决方式有两种一是先删该学生的所有上机记录再删学生二是把外键的删除规则改成 CASCADE。课设里我建议用第一种手动删更安全CASCADE 容易误删数据。-- 先删子记录 DELETE FROM LoginRecord WHERE StuID 2023001; -- 再删主记录 DELETE FROM Student WHERE StuID 2023001;如果要在 PowerBuilder 里做把这两条 SQL 放在一个事务里用 SQLCA 执行。4.5 现象SQL Server 2012 密码到期sa 登录不上SQL Server 2012 默认启用了密码过期策略sa 密码 90 天到期。到期后登录会提示 “密码已过期”。解决办法用 Windows 身份验证登录在安全性里找到 sa 账号右键属性取消勾选 “强制实施密码过期策略”然后重新设置密码。或者用 T-SQLALTER LOGIN sa WITH PASSWORD newpassword; ALTER LOGIN sa WITH CHECK_POLICY OFF;CHECK_POLICY OFF 关闭密码策略检查课设环境里这样最省事。生产环境别这么干。5. 让课设多拿几分用触发器做操作日志和余额校验课设答辩时老师常问“你怎么保证数据一致性”这时候如果你能说出触发器分数会好看很多。我一般会加两个触发器一个记录上机操作的日志一个在扣费时校验余额不能为负。先建日志表CREATE TABLE OperationLog ( LogID INT IDENTITY(1,1) PRIMARY KEY, TableName VARCHAR(30), Operation VARCHAR(10), RecordID INT, LogTime DATETIME DEFAULT GETDATE(), Operator VARCHAR(30) );然后在上机记录表上建 AFTER INSERT 触发器记录谁在什么时候上了哪台机器CREATE TRIGGER trg_LoginRecord_Insert ON LoginRecord AFTER INSERT AS BEGIN INSERT INTO OperationLog(TableName, Operation, RecordID, Operator) SELECT LoginRecord, INSERT, RecordID, SUSER_SNAME() FROM inserted; ENDSUSER_SNAME() 返回当前登录的数据库用户名课设里就是 sa。这个触发器在每次插入上机记录时自动写日志不需要应用层做任何事。再建一个余额校验触发器防止扣费后余额变负CREATE TRIGGER trg_Student_BalanceCheck ON Student AFTER UPDATE AS BEGIN IF EXISTS (SELECT 1 FROM inserted WHERE Balance 0) BEGIN ROLLBACK TRANSACTION; RAISERROR(余额不足扣费失败, 16, 1); END END这个触发器在 Student 表更新后检查如果发现余额小于 0直接回滚整个事务并报错。这样即使应用层忘了检查余额数据库层也能兜住。验证触发器是否生效在 SQL Server Management Studio 里手动插一条 LoginRecord然后查 OperationLog 表看有没有新记录。再手动把某个学生的 Balance 更新成负数看是否报错回滚。-- 测试日志触发器 INSERT INTO LoginRecord(StuID, MacID) VALUES (2023001, A101-01); SELECT * FROM OperationLog; -- 测试余额触发器应该报错 UPDATE Student SET Balance -10 WHERE StuID 2023001;如果日志表有记录、余额更新报错说明两个触发器都工作正常。答辩时把这两个测试跑一遍比口头说“我做了触发器”有说服力得多。最后说个习惯我每次改完存储过程或触发器都会在 SSMS 里用sp_helptext 对象名看一眼定义确认没有语法错误再让 PowerBuilder 调用。课设时间紧但这一步能省掉来回切窗口调试的麻烦。希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进