简介:这是一份以工资管理系统为题的SQL数据库课程设计报告,面向正在学习《数据库技术及其应用》的高校学生,可用于完成课程设计、理解数据库建模与SQL开发全流程。文档完整覆盖选题背景与意义、概念结构设计(ER模型)、逻辑结构设计(字段类型、主外键与索引)以及程序代码实现,后者包括创建员工表、部门表和工资明细表,数据导入,按部门统计平均工资、查找特定员工工资记录等典型查询,以及异常处理、权限控制等实用功能;末尾还提供课程设计总结与参考文献,便于读者借鉴整体框架和排错思路,快速撰写同类报告。资源包仅含1个doc文件,大小389KB,内容集中、目录清晰,适合直接阅读或在此基础上扩展功能。已有3597人浏览学习,尤其适合数据库初学者作为课程设计和项目实践的参考模板。
1. 工资管理系统这种 SQL 课程设计,为什么值得认真做
很多同学把数据库课程设计做成图书管理、学生管理,最后交上去的脚本几乎都是同一套增删改查。工资管理系统看起来也不难,但如果你愿意认真拆,它会逼你把多表关联、分组统计、存储过程、事务、视图和触发器一起用上。标题里的.doc也提醒了一件事:这次交付的不只是数据库脚本,还要写一份能给老师讲清楚的设计文档。这个方案适合正在学 SQL Server 或 MySQL、想在课程设计里拿高分的人,也适合想把“多表关联 + 业务计算”练扎实的转行者。我的建议是:把工资管理系统做小做透,不要做大做空。
2. 表结构设计与建库脚本:把工资业务拆成 5 张核心表
2.1 业务场景与 5 张表的关系:谁在发工资
工资系统的核心是“员工—部门—工资项—发放记录”。如果只建一张工资表,绩效、补贴、代扣会越塞越乱,答辩时老师一句话就能问住你。常见做法是拆成部门表、员工表、工资项目表、工资发放表、工资明细表这五张,再加一张用户表做登录。员工属于某个部门,一张工资单要记录发放年月,明细表保存各项金额。关系不复杂,但已经足够把外键、唯一约束和联查练到。
这里有一个容易被忽略的建模点:工资项必须是独立表,不要把“基础工资”“岗位工资”“奖金”做成员工表里的列。把工资项独立出来之后,新增一种补贴只需要在salary_item里加一行,不需要改表结构。这是“数据驱动”和“写死字段”的分界线,也是课程设计文档里可以单独写一段的亮点。部门表则解决了员工调动的问题,employee.dept_id一改,下个月工资核算自动归到新部门。
工资主表salary_pay要保留pay_year和pay_month两个字段,而不是只存一个pay_date。原因很简单:工资按月核算,按年月查询和汇总要比按日期区间查询简单得多,也更容易建联合索引。再加上唯一约束(emp_id, pay_year, pay_month),数据库层面就挡住了“同一个月给同一个人发两次工资”的脏数据。
2.2 建库建表 SQL:字段类型、主外键与索引一起定
下面以 SQL Server 为例写建库建表脚本。MySQL 用户把NVARCHAR换成VARCHAR,把IDENTITY(1,1)换成AUTO_INCREMENT,其他基本能直接跑。金额字段不要用float,原因在第 5 章展开,这里直接上DECIMAL(10,2)。
CREATE DATABASE SalaryDB; GO USE SalaryDB; GO CREATE TABLE dept ( dept_id INT IDENTITY(1,1) PRIMARY KEY, dept_name NVARCHAR(50) NOT NULL UNIQUE ); CREATE TABLE employee ( emp_id INT IDENTITY(1,1) PRIMARY KEY, emp_no NVARCHAR(20) NOT NULL UNIQUE, emp_name NVARCHAR(20) NOT NULL, dept_id INT NOT NULL REFERENCES dept(dept_id), hire_date DATE NOT NULL, base_salary DECIMAL(10,2) NOT NULL DEFAULT 0 ); CREATE TABLE salary_item ( item_id INT IDENTITY(1,1) PRIMARY KEY, item_code NVARCHAR(20) NOT NULL UNIQUE, item_name NVARCHAR(50) NOT NULL, item_type CHAR(1) NOT NULL CHECK (item_type IN ('E', 'D')) ); CREATE TABLE salary_pay ( pay_id INT IDENTITY(1,1) PRIMARY KEY, emp_id INT NOT NULL REFERENCES employee(emp_id), pay_year INT NOT NULL, pay_month INT NOT NULL CHECK (pay_month BETWEEN 1 AND 12), create_time DATETIME NOT NULL DEFAULT GETDATE(), CONSTRAINT uk_emp_month UNIQUE (emp_id, pay_year, pay_month) ); CREATE TABLE salary_detail ( detail_id INT IDENTITY(1,1) PRIMARY KEY, pay_id INT NOT NULL REFERENCES salary_pay(pay_id), item_id INT NOT NULL REFERENCES salary_item(item_id), amount DECIMAL(10,2) NOT NULL DEFAULT 0, CONSTRAINT uk_pay_item UNIQUE (pay_id, item_id) );逻辑说明:employee用dept_id外键关联部门,salary_pay是当月工资单主表,salary_detail是明细表,存基础工资、绩效、社保、个税等每一项金额。加了uk_emp_month唯一约束之后,同一个月重复发工资时数据库会直接报错,不需要再写程序判断。item_type用CHECK限制成E(加项)和D(减项),后面计算实发时可以直接按类型区分加减。这套结构对应了三句话:一个部门有多名员工,一名员工每月只有一张工资单,一张工资单有多条明细。E-R 图照着画就行。
参数说明:DECIMAL(10,2)是定点数,精确到分;NVARCHAR是 Unicode,在 SQL Server 里存中文必须用它。日期类型选DATE,不要存成字符串,否则做月份统计时要到处CONVERT。索引方面,主键是聚集索引,外键列比如salary_detail.pay_id建议补一个普通索引,因为工资明细的查询和删除都按pay_id走,没有索引时数据量一到几百条就开始变慢。(pay_year, pay_month)也要建联合索引,这就是后面慢 SQL 优化的基础。
2.3 必调的 3 个数据库参数:字符集、存储引擎、金额精度
建库时顺手把参数对好,能省掉后面一半排查时间。
字符集:SQL Server 的排序规则选Chinese_PRC_CI_AS,MySQL 则在建库时写DEFAULT CHARACTER SET utf8mb4。排序规则不一致会让跨表 JOIN 中文时出问题,这是典型的“本地能跑,换台机器就翻车”。如果你用 Navicat 这类数据库管理工具连接,注意连接选项里也要选 utf8mb4,服务端和客户端字符集必须一致。
存储引擎:MySQL 里只用 InnoDB,别用 MyISAM 当演示亮点。工资系统需要事务,一次发工资要同时写主表和明细表,任何一条失败都应该整体回滚。InnoDB 的行级锁也能避免两个会话同时改同一个人的工资单时互相锁表。SQL Server 没有这个选择,但要注意把数据库的恢复模式设为“简单”或“完整”,课程设计里用默认的完整模式即可,不需要额外折腾。
金额精度:工资和税率计算不要用float或double。0.1 + 0.2在浮点数里不是精确的0.3,个税算到分时就会出现 0.01 的偏差。统一用DECIMAL(10,2)存最终结果,中间计算用DECIMAL(18,4),最后ROUND到 2 位。这个习惯在课程设计里体现为“实发工资 = 应发 - 代扣”对账一致,老师一看你是懂行的。
3. 用 SQL 跑通工资增删改查与自动计算:存储过程、视图和触发器怎么选
3.1 先写基础增删改查,把页面要用的接口都跑通
先别急着上触发器。一个工资管理系统,最基本的接口是员工信息增删改查、工资单按月查询、工资项调整。建议按下面的顺序写:新增员工、员工离职删除、按部门查员工、按月份查工资单、修改某个工资项。下面这段是常见的增删改查雏形。
-- 新增员工 INSERT INTO employee(emp_no, emp_name, dept_id, hire_date, base_salary) VALUES ('E1001', N'张伟', 1, '2024-01-15', 6000.00); -- 按部门分页查询员工 DECLARE @deptId INT = 1; DECLARE @page INT = 1, @size INT = 10; SELECT emp_id, emp_no, emp_name, base_salary FROM employee WHERE dept_id = @deptId ORDER BY emp_id OFFSET (@page - 1) * @size ROWS FETCH NEXT @size ROWS ONLY; -- 按月份查工资单:应发和代扣一次算出来 SELECT e.emp_no, e.emp_name, p.pay_year, p.pay_month, SUM(CASE WHEN si.item_type = 'E' THEN d.amount ELSE 0 END) AS should_pay, SUM(CASE WHEN si.item_type = 'D' THEN d.amount ELSE 0 END) AS deduction FROM salary_pay p JOIN employee e ON p.emp_id = e.emp_id JOIN salary_detail d ON d.pay_id = p.pay_id JOIN salary_item si ON si.item_id = d.item_id WHERE p.pay_year = 2024 AND p.pay_month = 5 GROUP BY e.emp_no, e.emp_name, p.pay_year, p.pay_month;逻辑说明:增删改查本身不难,但课程设计里的加分会藏在边角里。分页查询我用了OFFSET ... FETCH,比只写SELECT *更接近实际管理系统里的表格分页。按月份统计工资用了条件聚合,CASE WHEN把加项和减项分别加总,一条 SQL 就能输出“应发工资”和“代扣金额”两个字段,后端不用再算第二遍。
参数说明:@page、@size是 SQL Server 局部变量,MySQL 里对应LIMIT #{offset}, #{size}。所有用户输入都走参数,不要用字符串拼接 SQL。课程设计虽然不强制要求,但在文档的“安全性设计”里写一句“使用参数化查询,防止 SQL 注入”,比堆一大排功能截图更能说明你理解了这门课的重点。
3.2 存储过程:把应发工资和个税算出来
工资计算适合放在数据库里。原因在于它涉及多张表,从employee.base_salary、salary_detail里的各项金额到个税税率判定,写在业务代码里会拉出一长串逻辑;放在存储过程里,每次发工资都走同一套规则,也方便课程设计答辩时现场演示。这是老师最可能问的地方:“你的工资是怎么算出来的?”
CREATE PROCEDURE usp_CalcMonthSalary @emp_id INT, @year INT, @month INT AS BEGIN SET NOCOUNT ON; DECLARE @base DECIMAL(10,2), @post DECIMAL(10,2), @bonus DECIMAL(10,2); DECLARE @social DECIMAL(10,2), @taxable DECIMAL(10,2), @tax DECIMAL(10,2); DECLARE @pay_id INT; BEGIN TRY BEGIN TRANSACTION; -- 当月已有工资单,先清理明细和主表,保证重复执行不会叠加 DELETE d FROM salary_detail d JOIN salary_pay p ON d.pay_id = p.pay_id WHERE p.emp_id = @emp_id AND p.pay_year = @year AND p.pay_month = @month; DELETE FROM salary_pay WHERE emp_id = @emp_id AND pay_year = @year AND pay_month = @month; -- 取基础工资;岗位工资按部门档位简化,奖金固定 500 SELECT @base = e.base_salary, @post = CASE e.dept_id WHEN 1 THEN 1500 ELSE 800 END, @bonus = 500 FROM employee e WHERE e.emp_id = @emp_id; -- 应发 = 基础 + 岗位 + 奖金;社保个人部分按 8% 简化 SET @taxable = @base + @post + @bonus; SET @social = @taxable * 0.08; SET @taxable = @taxable - @social - 5000; -- 个税阶梯:演示用,文档里注明按最新税率表配置 SET @tax = 0; IF @taxable > 0 BEGIN IF @taxable <= 3000 SET @tax = @taxable * 0.03; ELSE IF @taxable <= 12000 SET @tax = @taxable * 0.10 - 210; ELSE SET @tax = @taxable * 0.20 - 1410; END; INSERT INTO salary_pay(emp_id, pay_year, pay_month) VALUES (@emp_id, @year, @month); SET @pay_id = SCOPE_IDENTITY(); INSERT INTO salary_detail(pay_id, item_id, amount) VALUES (@pay_id, 1, @base), (@pay_id, 2, @post), (@pay_id, 3, @bonus), (@pay_id, 4, @social), (@pay_id, 5, @tax); COMMIT TRANSACTION; END TRY BEGIN CATCH ROLLBACK TRANSACTION; THROW; END CATCH; END; GO逻辑说明:这个存储过程做了三件事:清理当月旧数据、按当前规则重新计算、再把结果写入主表和明细表。删除操作放在事务里,如果后面任何一步失败,整个重算过程都会回滚,不会留下“主表有记录、明细是空的”这种半截数据。SCOPE_IDENTITY()取刚插入的salary_pay主键,避免在并发下取到别的会话的 id。
参数说明:个税部分是简化演示,只展示阶梯算法。文档里不要写“个税完全准确”,要声明“社保比例和专项扣除做了简化”。这样答辩时老师点出来,你有台阶下,反而显得思路清楚。@taxable减去的 5000 是起征点,真正做系统时可以把税率配置成表,存储过程只去读取,这样调整税率不用改代码,属于加分设计。存储过程的入参类型@emp_id、@year、@month要和表字段完全一致,避免隐式转换,这也是后面演示翻车的高发原因。
3.3 视图和触发器:课设答辩里最值得讲的两个东西
视图很适合做工资系统的只读展示层。它的价值在于把每次要写的 5 张表 JOIN 封装起来,业务层只写一行查询就能拿到完整的工资单。
CREATE VIEW v_salary_sheet AS SELECT e.emp_no, e.emp_name, d.dept_name, p.pay_year, p.pay_month, SUM(CASE WHEN si.item_type = 'E' THEN det.amount ELSE 0 END) AS should_pay, SUM(CASE WHEN si.item_type = 'D' THEN det.amount ELSE 0 END) AS deduction, SUM(CASE WHEN si.item_type = 'E' THEN det.amount WHEN si.item_type = 'D' THEN -det.amount ELSE 0 END) AS net_pay FROM salary_pay p JOIN employee e ON e.emp_id = p.emp_id JOIN dept d ON d.dept_id = e.dept_id JOIN salary_detail det ON det.pay_id = p.pay_id JOIN salary_item si ON si.item_id = det.item_id GROUP BY e.emp_no, e.emp_name, d.dept_name, p.pay_year, p.pay_month;这个视图把“应发”“代扣”“实发”三列都算好了。查询某月工资表时,一句SELECT * FROM v_salary_sheet WHERE pay_year=2024 AND pay_month=5就能拿去给报表做数据源。写文档时,视图的三列结果可以和后面的对账报表呼应。
触发器用来做敏感操作的审计。工资单不该被无痕修改,每次薪资变化都往日志表写一笔。
CREATE TABLE salary_pay_log ( log_id INT IDENTITY(1,1) PRIMARY KEY, pay_id INT NOT NULL, old_net DECIMAL(10,2) NOT NULL DEFAULT 0, new_net DECIMAL(10,2) NOT NULL DEFAULT 0, changed_by NVARCHAR(50), change_time DATETIME NOT NULL DEFAULT GETDATE() ); GO CREATE TRIGGER trg_salary_pay_audit ON salary_pay AFTER UPDATE AS BEGIN SET NOCOUNT ON; INSERT INTO salary_pay_log(pay_id, old_net, new_net, changed_by) SELECT i.pay_id, ISNULL((SELECT amount FROM salary_detail WHERE pay_id = i.pay_id AND item_id = 5), 0), ISNULL((SELECT amount FROM salary_detail WHERE pay_id = i.pay_id AND item_id = 5), 0), SUSER_SNAME() FROM inserted i; END; GO这段触发器只是演示骨架,真正的旧实发要 JOINDELETED临时表和salary_detail去算,课程设计里把思路讲清楚就行。注意触发器里不要写同表的 UPDATE,否则会递归触发,变成慢 SQL 甚至死锁。这个坑下面单独说。
4. 课程设计 .doc 怎么写出高分:需求分析、E-R 图与测试记录的落地顺序
4.1 课程设计文档的七段式结构:先做什么后写什么
你最终要交的是一份.doc。常见结构是:课程设计任务与要求、需求分析、概念结构设计(E-R 图)、逻辑结构设计(关系模式)、物理设计、数据库实现(核心 SQL)、测试记录与总结。不要照着这个顺序从前往后写,不然需求分析会写得很空洞。我一般先把表结构定稿,再画 E-R 图,最后回头补需求分析,因为表结构本身就是需求的骨架。答辩老师看文档,第一眼看目录,第二眼就翻数据字典,表建得规范,文档得分不会低。
文档的排版也要注意:标题用 Word 内置的“标题 1”“标题 2”样式,不要手改字号,否则生成的目录会乱。截图统一缩放成 80% 宽度,不要一张大一张小。代码块用等宽字体,比如 Consolas,背景统一为浅灰色,直接截屏贴图也可以,但要保证小字号下能看清。这些细节不直接加分,但能拉高整体印象分。
4.2 数据字典、E-R 图和 SQL 语句在 Word 里的写法
数据字典不是把CREATE TABLE的代码截图贴进去,而是整理成三线表。每个字段一行,字段含义要写清楚,尤其是约束。下面这个格式可以直接抄。
| 字段名 | 类型 | 可否为空 | 约束/说明 |
|---|---|---|---|
| emp_id | INT | 否 | 主键,自增 |
| emp_no | NVARCHAR(20) | 否 | 员工编号,唯一 |
| dept_id | INT | 否 | 外键,引用 dept(dept_id) |
| hire_date | DATE | 否 | 入职日期 |
| base_salary | DECIMAL(10,2) | 否 | 基础工资,默认 0 |
E-R 图不用画得特别复杂,实体是“部门、员工、工资单、工资项、工资明细”,关系就是第 2 章那 5 张表的关联。工具用 draw.io 或 Visio,导出高清图再插入 Word。属性不要全部画出来,每个实体只挑 3~5 个关键属性,图一复杂反而扣分。
核心 SQL 不要全文粘贴。文档里放三类就够:建库建表语句、存储过程全文、一条复杂查询,其他增删改查代码放进附录。原因是打印出来 40 页 SQL,老师不会看,答辩时问你核心逻辑在哪,你指不出来,等于白写。存储过程里最好加注释,文档里把注释保留,证明代码是你自己组织过的。
4.3 测试记录:预置数据脚本与运行截图怎么组织
测试记录是课程设计里最容易失分的地方。常见的错误是写“经测试,系统运行正常”,没有任何数据支撑。工资系统至少要有三组测试:正常发工资、修改工资项后重新计算、演示非法数据被约束拦截。预置数据要独立成一个data_init.sql,方便随时重置演示现场。
INSERT INTO dept(dept_name) VALUES (N'技术部'), (N'人事部'); INSERT INTO employee(emp_no, emp_name, dept_id, hire_date, base_salary) VALUES ('E1001', N'张伟', 1, '2024-01-15', 6000.00), ('E1002', N'李丽', 1, '2024-04-01', 7000.00), ('E2001', N'王强', 2, '2023-10-20', 5500.00); INSERT INTO salary_item(item_code, item_name, item_type) VALUES ('BASE', N'基础工资', 'E'), ('POST', N'岗位工资', 'E'), ('BONUS', N'绩效奖金', 'E'), ('SOCIAL', N'社保代扣', 'D'), ('TAX', N'个人所得税', 'D');然后依次执行:
EXEC usp_CalcMonthSalary @emp_id = 1, @year = 2024, @month = 5; EXEC usp_CalcMonthSalary @emp_id = 2, @year = 2024, @month = 5; EXEC usp_CalcMonthSalary @emp_id = 3, @year = 2024, @month = 5; SELECT * FROM v_salary_sheet;把SELECT的结果截图放进 Word,下面写一段文字说明“三名员工均正常生成工资单,应发与代扣金额一致”。这张截图就是你的测试记录。我踩过的坑是:文档里的截图日期和脚本数据对不上,老师一翻就发现是拼凑的。测试记录一定要在脚本跑通之后现截,不要拿别人的图。
5. 工资管理系统课设避坑:连接失败、乱码、金额精度与演示翻车的排查
5.1 SQL Server 登录失败:sa 账号被禁用或密码过期
现象:用 SSMS 或 Navicat 连接本机数据库,报“用户 sa 登录失败”,或者连不上服务器。原因有两类:安装时选了“Windows 身份验证模式”,SQL Server 认证没有打开;或者 sa 账号默认禁用、密码过期。SQL Server 2012 之后还有一个常见情况:sa 密码带有效期策略,长期不登录会被锁住。
解决:先用 Windows 身份验证登录,在服务器属性里把身份验证模式改成“SQL Server 和 Windows 身份验证模式”,然后找到 sa 账号,启用并重置密码。密码别用纯数字,至少八位带字母,别在演示现场想不起来。连接串里服务器名写localhost或127.0.0.1,带实例名时写成计算机名\SQLEXPRESS。这个坑一定要写进文档的“运行环境配置”,因为老师搭环境时最容易被卡住。
5.2 中文乱码:排序规则与客户端编码不一致
现象:插入中文后查询出来是???,或者网页显示乱码。SQL Server 里的常见原因是列类型用了VARCHAR而不是NVARCHAR,或者数据库排序规则选成了非中文的 Latin 系列。MySQL 的常见原因是连接层字符集和表字符集不统一,SET NAMES utf8mb4没有执行。
解决:SQL Server 建表时统一用NVARCHAR,建库时排序规则选Chinese_PRC_CI_AS;MySQL 在连接参数里显式指定characterEncoding=utf8mb4,连接后先执行SET NAMES utf8mb4。乱码属于那种看起来像玄学、其实就是编码链路没对齐的问题,排查时按“客户端 → 连接层 → 数据库 → 表”的顺序一项项看。
5.3 金额精度翻车:float 算工资会多一分钱
现象:存储过程算出来的实发工资是4999.999999,页面上显示成 5000,数据库里存的却是 49.999999,对账对不平。原因:float是二进制浮点,十进制的 0.1 无法精确表示,多个工资项相加减误差累积,到分位就翻车。
解决:所有金额字段改成DECIMAL(10,2),中间计算用DECIMAL(18,4),最后用ROUND转到 2 位。这个坑在工资系统里特别常见,因为个税是阶梯税率,每一档算完再累加,误差会被放大。建表时就把金额字段定义成DECIMAL(10,2),不接受任何临时起意的float。
5.4 触发器递归导致死锁,慢 SQL 越查越慢
现象:工资单更新时触发器执行,更新日志表后又去更新工资单,造成递归触发,SQL Server 报触发器嵌套相关错误,或者直接死锁。另一个现象是明细表数据量到几千条后,v_salary_sheet查询变慢。
原因:递归触发常见于AFTER UPDATE触发器里又写了同一张表的 UPDATE;慢 SQL 常见于查询条件把索引列包进函数,比如写WHERE YEAR(create_time) = 2024,导致索引失效。解决:触发器里只写日志表 INSERT,不写主表 UPDATE;必须嵌套时在触发器开头加IF TRIGGER_NESTLEVEL() > 1 RETURN。慢 SQL 优化这一步,把(pay_year, pay_month)建成联合索引,查询条件写pay_year = 2024 AND pay_month = 5,不要写YEAR(pay_time)。排查时用 SSMS 的“显示估计的执行计划”,看到 Index Scan 而不是 Index Seek,就说明条件写得有问题。
5.5 演示时查不到数据:参数类型和事务提交是头号原因
现象:答辩现场跑脚本,页面或查询工具里看不到刚才插入的数据,或者按月份查询结果为空。原因:第一,参数类型不一致,pay_month在表里是INT,前端传过来字符串'05',隐式转换后匹配不上。第二,INSERT 后没有提交事务,SSMS 里自己能看到,关掉连接或换一个会话就查不到。第三,演示机连接的是旧数据库,工资数据根本没有同步过去。
解决:统一参数类型,存储过程入参和表字段保持一致;所有 INSERT/UPDATE 语句明确写BEGIN TRAN和COMMIT;演示前固定用一台机器,或者重新跑一遍data_init.sql。这个坑属于血泪经验:我遇到过演示机上还是上周的备份,当场查不到新数据。之后每次演示前都先执行初始化脚本,花 30 秒换来整场不慌。
6. 把工资系统从“能交作业”练到“敢现场演示”:一键初始化和对账报表技巧
6.1 一键初始化:把建库、建表和预置数据合成一条命令
把脚本整理成三个文件:01_schema.sql放建库建表,02_init_data.sql放预置数据,03_biz.sql放存储过程、视图和触发器。演示前用 sqlcmd 一条条执行:
sqlcmd -S localhost -d master -i 01_schema.sql -b sqlcmd -S localhost -d SalaryDB -i 02_init_data.sql -b sqlcmd -S localhost -d SalaryDB -i 03_biz.sql -b-b表示出错时终止并返回错误码,配合批处理可以做到“脚本跑完才算初始化成功”。MySQL 用户把三条命令换成mysql -u root -p < 01_schema.sql即可。这条命令的价值在于:你可以当着老师的面把数据库删掉重建,证明整个系统不依赖任何手工补数据。
6.2 用 GROUP BY ROLLUP 生成工资对账报表
另一个能让文档和演示都亮眼的技巧是用ROLLUP按部门汇总工资。查询会输出每个部门和最后一行的合计:
SELECT COALESCE(d.dept_name, N'合计') AS dept_name, SUM(v.should_pay) AS total_should_pay, SUM(v.deduction) AS total_deduction, SUM(v.net_pay) AS total_net_pay FROM v_salary_sheet v JOIN dept d ON d.dept_id = v.dept_id WHERE v.pay_year = 2024 AND v.pay_month = 5 GROUP BY ROLLUP(d.dept_name);把这条查询的结果截图放进 Word 的测试记录里,比口头说“系统能统计”有说服力得多。数据量变大后,v_salary_sheet底层会扫描salary_detail,记得给(pay_id, item_id)和(pay_year, pay_month)都建上索引,这就是一份现成的慢 SQL 优化素材。
我过去做课设时也吃过亏,把重点全放在界面好不好看,数据库脚本却乱成一团。后来才明白,工资管理系统这种课程设计,真正值钱的部分是表结构、存储过程和那份能讲清楚设计过程的.doc。把初始化脚本和对账查询练熟,演示就不会翻车。希望帮到你。
本文还有配套的精品资源,点击获取