简介:面向高校数据库课程设计场景的SQL Server人事管理系统完整项目,适合需要掌握数据库建表、数据关系设计与Java GUI联调的在校学生,可直接作为课设参考模板或二次开发底座。系统核心围绕员工、部门、职位等数据表展开,涵盖查询、添加、修改、删除等常用操作,并兼顾数据一致性、完整性与安全性设计。压缩包共197个文件、18.06MB:内含创建人事库的SQL脚本、18个Java源文件与8个jar运行依赖、116个class编译产物,以及44张png界面截图便于逐一核对功能效果;配套docx实习报告与pptx汇报材料则完整记录了数据库建模思路、系统实现过程与常见问题解决方案。目前已有1403人学习或下载,资源按SQL脚本、Java源码、图文文档三层存放,目录结构清晰,方便快速定位所需模块,适合从零理解SQL Server实际应用并在此基础上进行功能扩展。
1. 人事管理系统用 SQL Server 做课程设计,到底要交付哪些东西
我见过太多把人事管理系统做成“用户表加员工表”的课设,交上去被老师一问“你的部门怎么冗余”“你的薪资怎么查出来的”就直接尬住。SQL Server 人事管理系统这个题目,真正的考察点并不是你把网页做得多漂亮,而是你能不能把部门、员工、考勤、薪资这一条线用数据库设计串起来,并且把增删改查写到能说服别人“这个系统真的能跑”的程度。合适的完成度有三样:一套范式合理的关系模型、一组覆盖核心场景的增删改查、加上一两个能体现数据库能力的存储过程或事务处理。适合谁呢——正在准备数据库课程设计的本科生、想快速搭一个能答辩的系统的在职学生,以及那些刷了不少 SQL 题但没完整落过一张表的开发者。这篇文章就是照着“能复现”的标准来的,表结构、脚本、坑我都会给到,直接抄完改改字段就能用。
2. 数据库课程设计的第一步:把人事系统的四张核心表建明白
2.1 为什么只用四张表不漏业务,也不背课设的锅
很多同学一上来就建十几张表,结果数据填不满、逻辑对不上,答辩被问两句就慌。常见做法是:人事管理系统的最小闭环就四张表——部门表、员工表、考勤表、薪资表。这四张表能覆盖“员工属于哪个部门、这个月出勤多少天、该发多少钱”这个主流程;再加一个用户表用于登录权限。部门表和员工表是 1 对多,员工表和考勤表、薪资表是 1 对多。这个关系模型不花哨,但每一个外键都回答了一个业务问题,被老师追问的时候你能说出“为什么要这样设计”的理由,这就是课程设计的价值所在。
不需要设计成 employee、department、attendance、salary、user 之外摆了 site、role 之类的表,除非题目明确要求了权限分级。最简单的人事课设场景里,一张用户表只存登录名和密码哈希,一个用户绑定一个员工编号,就足够撑起登录和角色判断了。表越少,约束越集中,越不容易出现“系统看着完整,数据却是脏的”这种课设通病。
2.2 从部门表到薪资表:一份能直接抄的建表脚本与参数设计
我这里以 SQL Server 2019 及以上的语法为准,用到的都是最稳定的类型和约束。下面是四张核心表加一张用户表的建表脚本,可以直接在 SSMS 新建查询里运行:
-- 部门表 CREATE TABLE Department ( DeptId INT IDENTITY(1,1) PRIMARY KEY, DeptName NVARCHAR(50) NOT NULL UNIQUE, ManagerName NVARCHAR(20) NULL, CreateDate DATE DEFAULT GETDATE() ); -- 员工表 CREATE TABLE Employee ( EmpId INT IDENTITY(1,1) PRIMARY KEY, EmpNo VARCHAR(20) NOT NULL UNIQUE, EmpName NVARCHAR(20) NOT NULL, Gender CHAR(2) CHECK (Gender IN ('男','女')), HireDate DATE NOT NULL, DeptId INT NOT NULL, Phone VARCHAR(20) NULL, CONSTRAINT FK_Emp_Dept FOREIGN KEY (DeptId) REFERENCES Department(DeptId) ); -- 考勤表 CREATE TABLE Attendance ( AttId INT IDENTITY(1,1) PRIMARY KEY, EmpId INT NOT NULL, WorkDate DATE NOT NULL, Status NVARCHAR(10) CHECK (Status IN ('正常','迟到','早退','缺勤')), Remark NVARCHAR(100) NULL, CONSTRAINT FK_Att_Emp FOREIGN KEY (EmpId) REFERENCES Employee(EmpId), CONSTRAINT UQ_Att_EmpDate UNIQUE (EmpId, WorkDate) ); -- 薪资表 CREATE TABLE Salary ( SalaryId INT IDENTITY(1,1) PRIMARY KEY, EmpId INT NOT NULL, Month CHAR(6) NOT NULL, -- 格式:202405 BaseSalary DECIMAL(10,2) NOT NULL, Bonus DECIMAL(10,2) DEFAULT 0, Deduction DECIMAL(10,2) DEFAULT 0, FinalSalary AS (BaseSalary + Bonus - Deduction), -- 计算列 CONSTRAINT FK_Sal_Emp FOREIGN KEY (EmpId) REFERENCES Employee(EmpId), CONSTRAINT UQ_Sal_EmpMonth UNIQUE (EmpId, Month) ); -- 用户表 CREATE TABLE AppUser ( UserId INT IDENTITY(1,1) PRIMARY KEY, EmpId INT NOT NULL UNIQUE, LoginName VARCHAR(20) NOT NULL UNIQUE, LoginPwd VARBINARY(64) NOT NULL, -- 存哈希值,不存明文 CONSTRAINT FK_User_Emp FOREIGN KEY (EmpId) REFERENCES Employee(EmpId) );几个关键参数我想特别说明。DEPTNAME和EMPNOL都加了UNIQUE约束,这是课程设计里常见的加分点,因为部门和员工编号天然应该唯一;考勤表里EmpId加WorkDate的联合唯一约束很关键,避免同一个人同一天有两条考勤记录。Salary表的FinalSalary是计算列,用BaseSalary + Bonus - Deduction自动得出实发工资,这种方式在课设答辩中经常被问到,回答“这是由数据库保证的计算一致性,而不是在程序里算”,会明显加分。薪资表的月份用CHAR(6)而不是DATE,因为工资月份本质上是一个“周期标签”,按月分组时更方便。
2.3 外键该不该建:建了删不掉,不建又没逻辑
外键是课设里面最容易纠结的地方。不建外键,删数据很自由,但数据会变成孤儿数据,比如员工部被删了但员工记录还挂在空的DeptId下面,程序一查询部门名称直接报空引用。建了外键,删除顺序错了会报“外键冲突”错误,这在平时练习时会给新手一种“数据库坏了”的错觉。我认为这里的最优解是:课程设计里建外键,但要搞懂删除的先后顺序。
外键的实质是对业务规则建模。员工没部门是没有意义的,考勤和薪资没员工也是没有意义的。所以外键不单是数据库题,而是系统逻辑题。解决删除问题的常用做法是禁用级联删除,让程序或者操作者先删除子表数据再删除主表数据,这样权限可控,也能在数据删除之前做一个确认。如果要更省事,也可以在表设计时加上ON DELETE CASCADE,但这并不建议用于课设,因为答辩时被问“误删部门会不会连带删掉员工”,你会很难解释得像一个认真考虑过数据安全的人。
3. 在 SQL Server 里跑通人事增删改查:有效的参数化写法
3.1 登录校验怎么做才不会被人脸黑匣子坑
很多人事管理系统的登录模块,直接在代码里写SELECT * FROM AppUser WHERE LoginName='...' AND LoginPwd='...',这是最早级也最容易被人拿捏的写法。正确做法是先把用户输入的密码做哈希存储,登录时先把输入的数据哈希过后再与库里的VARBINARY字段比对。在 SQL Server 中可以用HASHBYTES做这个运算,调用方式非常直接:
-- 插入用户时(密码 pwd=123456) INSERT INTO AppUser (EmpId, LoginName, LoginPwd) VALUES (1, 'admin', HASHBYTES('SHA2_256', '123456')); -- 登录校验 DECLARE @inputPwd VARCHAR(20) = '123456'; SELECT u.UserId, e.EmpName, d.DeptName FROM AppUser u JOIN Employee e ON u.EmpId = e.EmpId JOIN Department d ON e.DeptId = d.DeptId WHERE u.LoginName = 'admin' AND u.LoginPwd = HASHBYTES('SHA2_256', @inputPwd);这段脚本说明了两件事:一是密码经过哈希存储而不是明文,即使数据库被导出,密码也不会直接泄露;二是HashBytes是 SQL Server 内置函数,不需要额外配置任何环境。参数化的形式比我举的示例更安全,实际项目中应该用sp_executesql或者客户端参数化查询,不要把LoginName直接拼接进去,否则 SQL 注入会把你辛苦建的表全部带走。
3.2 员工分页与模糊查询:SQL Server 常用写法
人事管理系统的查询页基本逃不过三件事:按姓名模糊搜人、按部门筛选、分页展示。课程设计里最常见的分页写法是OFFSET-FETCH,SQL Server 2012 及以上版本都支持,属于效率与写法兼顾的方案。
DECLARE @pageSize INT = 10; DECLARE @pageIndex INT = 1; DECLARE @keyword NVARCHAR(20) = N'张'; SELECT e.EmpNo, e.EmpName, d.DeptName, e.HireDate FROM Employee e JOIN Department d ON e.DeptId = d.DeptId WHERE e.EmpName LIKE N'%' + @keyword + N'%' OR e.EmpNo LIKE '%' + @keyword + '%' ORDER BY e.EmpId OFFSET (@pageIndex - 1) * @pageSize ROWS FETCH NEXT @pageSize ROWS ONLY;注意LIKE的写法里我给中文关键词加了N前缀,这个细节很微妙。如果变量是NVARCHAR类型那加不加一般没问题;如果表字段是VARCHAR而传入的是 Unicode 字符串,不加N可能导致中文字符匹配不到。ORDER BY 后面必须要有唯一性好的列,用了OFFSET-FETCH不写 ORDER BY 会报错,这是新手很容易被拦住的点。
3.3 删除部门为什么总是卡住:外键与数据顺序
当你在管理系统页面上点“删除部门”却提示失败,第一时间要想到的不是程序 bug,而是外键约束。因为员工表里还有记录引用了这个部门的DeptId,数据库拒绝让你直接删掉一个有下级的部门。解决方法无外乎两种:在程序里先判断该部门下是否有员工,有员工就先提示“该部门下有员工,不能删除”;或者做逻辑删除,给部门表加一个IsDeleted字段,查询时只过滤IsDeleted = 0的数据。
删除的逻辑顺序永远是从子表到主表:先删考勤、薪资,再删员工,最后删部门。我在实际课设辅导里见过很多人因为删不掉数据而直接改用DROP TABLE,这是最不可取的。正确姿势是把删除操作封装成一个存储过程或者事务,在删除主表之前检查子表是否存在数据,这是一种“数据库在保护你的业务完整性”的现象,不是数据库在跟你作对。
4. 用存储过程把考勤和薪资算清楚
4.1 为什么要写存储过程而不是在客户端逐条算
人事管理系统的答辩环节,老师几乎必问一个问题:“薪资是怎么算出来的?”如果你回答“在 C# / Java 里循环遍历员工然后加减乘除”,这个答案会被扣分。原因很直接:算工资是数据密集任务,在数据库里操作能减少客户端与数据库之间的往返,而且存储过程天然支持事务与回滚,任何一步失败数据不会被写一半。存储过程是数据库课程设计里区分“会写 SQL”和“懂数据库应用”的分水岭。通常的做法是把“按月结算工资”这样一件事做成一个存储过程,传入月份参数,然后返回该月的薪资汇总表。
4.2 一个能算部门薪资汇总的存储过程示例
下面这个存储过程的场景是:传入一个月份,输出该月每个部门的人数、应发总额、实发总额、平均薪资,并按实发总额排序。这是一个很有代表性的汇总型存储过程,面试和答辩都能聊。
CREATE PROCEDURE [dbo].[usp_GetDeptSalarySummary] @Month CHAR(6) AS BEGIN SET NOCOUNT ON; SELECT d.DeptName, COUNT(DISTINCT s.EmpId) AS EmpCount, SUM(s.FinalSalary) AS TotalFinalSalary, AVG(s.FinalSalary) AS AvgSalary FROM Salary s JOIN Employee e ON s.EmpId = e.EmpId JOIN Department d ON e.DeptId = d.DeptId WHERE s.Month = @Month GROUP BY d.DeptName ORDER BY TotalFinalSalary DESC; END;使用时直接执行EXEC usp_GetDeptSalarySummary @Month = '202405'。这个存储过程的巧妙之处在于用了JOIN和GROUP BY把部门、员工、薪资三张表串起来,FinalSalary的计算列又省掉了手工加减。答辩时如果被问“为什么用 DISTINCT”,你可以解释:同一个员工一个月只有一条薪资记录,加 DISTINCT 是防御性的,防止未来有人修改了唯一约束导致重复统计。这个细节会让人觉得你真的踩过坑、想过边界。
4.3 存储过程的结果集、输出参数与回滚
存储过程不是只有“返回一张表”这一种玩法。还有一种常用设计是:用输出参数返回标量结果,比如传入员工 ID 和月份,返回这个员工的实发工资。这个模式在人事系统的“个人工资条”页面里很实用,也能展示你对存储过程理解的深度:
CREATE PROCEDURE [dbo].[usp_GetEmployeeSalary] @EmpId INT, @Month CHAR(6), @FinalSalary DECIMAL(10,2) OUTPUT AS BEGIN SELECT @FinalSalary = FinalSalary FROM Salary WHERE EmpId = @EmpId AND Month = @Month; IF @FinalSalary IS NULL BEGIN RAISERROR(N'该员工当月无薪资记录', 16, 1); RETURN -1; END END;调用方式在 SSMS 里的写法和程序里保持一致:
DECLARE @salary DECIMAL(10,2); EXEC usp_GetEmployeeSalary @EmpId = 1, @Month = '202405', @FinalSalary = @salary OUTPUT; PRINT @salary;RAISERROR 的作用是抛出业务错误,调用方程序能捕获到这个异常并给用户友好提示。批量插入考勤或薪资数据的时候,推荐把存储过程套上事务,用BEGIN TRAN配合TRY-CATCH中ROLLBACK和COMMIT成对出现,保证数据的原子性。事务是人事系统数据一致性的最后一道防线,尤其是月底算薪这种操作,失败重跑是常态,没有回滚就会留下一堆半截数据。
5. SQL Server 人事系统避坑:五个最容易翻车的地方
5.1 中文乱码与 N 前缀
现象:插入的中文部门名变成“???”,或者查询时中文条件查不出数据。原因通常是两个:表字段用了VARCHAR而不是NVARCHAR,或者插入时字符串前面没加N。SQL Server 默认在英文排序规则下,VARCHAR对 Unicode 字符支持有限,中文字符会丢失或错乱。解决方式非常直接:所有可能存中文的字段统一用NVARCHAR,所有中文字符串常量前面加N前缀。这一点在课程设计代码里属于一眼就能被看出的低级错误,务必在提交前全局检查一遍。
5.2 datetime 还是 date:时间字段用错的连锁反应
现象:考勤表里WorkDate用DATETIME,然后在程序里按yyyy-MM-dd格式传参查询,结果同一天的记录有时候查出来两条。原因在DATETIME包含了时间部分,如果同一天插入时带了不同时间戳,按日期相等比较就会漏数据。组长的常见做法是:只要不涉及时分秒的业务,一律用DATE类型,比如入职日期只精确到天。这样一来BETWEEN和=的语义都干净。已经用了DATETIME的表,查询时可以采用WHERE WorkDate >= '2024-05-01' AND WorkDate < '2024-05-02'这种左闭右开区间写法,不要用CONVERT(VARCHAR, WorkDate, 23) = '2024-05-01'这种包一层函数的写法,否则索引失效,数据大了会非常慢。
5.3 登录失败:混合认证与连接字符串的常见问题
现象:程序连接 SQL Server 报“用户 'sa' 登录失败”,或者“无法连接到服务器”。这一般不是代码写错了,而是 SQL Server 默认只开了 Windows 身份验证模式。解决方式是通过 SSMS 连接实例后,右键实例选择属性,在安全性页面把服务器身份验证改为“SQL Server 和 Windows 身份验证模式”,然后重启 SQL Server 服务。连接字符串里的常用写法是Server=.;Database=HRMS;User Id=sa;Password=xxxx;,这里的Server=.表示本机默认实例。课程设计用的开发机最容易出现的问题还包括:连接字符串拼写错误、1433 端口没开、实例名写错。如果本机服务都没起来,再正确的连接字符串也白搭,先打开 SQL Server 配置管理器确认服务状态。
5.4 删除顺序引出的外键冲突
现象:点击删除一个员工,数据库报外键冲突,原因是考勤表和薪资表里还有他的记录。这个坑我在第 3 章已经讲过,但这里再给一个稳妥的删除模板。人事系统里删除员工前必须先清掉考勤和薪资数据,用事务包起来最安全:
BEGIN TRAN; DELETE FROM Attendance WHERE EmpId = @EmpId; DELETE FROM Salary WHERE EmpId = @EmpId; DELETE FROM AppUser WHERE EmpId = @EmpId; DELETE FROM Employee WHERE EmpId = @EmpId; COMMIT;每一步删除都有明确顺序,要想周全还包括用户表。事务的意义在于,如果中途有一步失败,前面删除的记录也会回滚,不会出现人没了但工资数据还在的脏状态。
5.5 数据库大小写与主键自增的边界
现象:员工编号EMP001在查询时输入emp001查不到数据。原因在于 SQL Server 默认排序规则是SQL_Latin1_General_CP1_CI_AS,不区分大小写,但有的系统用二进制排序规则就区分了。这个坑通常不发生在默认安装中,但移植别人的库文件时容易踩中。建议统一用大写字母存EmpNo这类业务编号,不依赖数据库的大小写敏感策略。另外,IDENTITY(1,1)是 SQL Server 的自增主键写法,值得注意的是删除记录后自增值不会回退,如果课设要求编号连续,需要自行处理或明确不要求连续。答辩时被问“为什么员工编号有跳号”,能回答出“这是自增主键的天然行为,保证并发下编号唯一”,会比强行做成连续编号更显专业。
6. 进阶:用事务和 IDENTITY 把数据串成闭环
最后一章给一个高完成度的进阶方向:用事务同时插入员工和账号,把IDENTITY拿回来串起两张表。人事系统的常见业务场景是新建员工的同时必须创建登录账号,如果分两次提交,中间崩溃一次就会出现“有员工没账号”的脏数据。可以用事务包裹整个逻辑:
BEGIN TRAN; DECLARE @newEmpId INT; INSERT INTO Employee (EmpNo, EmpName, Gender, HireDate, DeptId) VALUES ('EMP023', N'王小明', '男', '2024-06-01', 2); SET @newEmpId = SCOPE_IDENTITY(); INSERT INTO AppUser (EmpId, LoginName, LoginPwd) VALUES (@newEmpId, 'wangxm', HASHBYTES('SHA2_256', '123456')); COMMIT;SCOPE_IDENTITY()是拿当前会话、当前作用域自增值的最可靠函数,比@@IDENTITY更安全,因为后者会被触发器里的插入动作干扰。事务加SCOPE_IDENTITY()这个组合是能体现你对 SQL Server 熟悉度的标志性写法。验证方式也很简单:插入后手动删除该员工账号表里的数据,再把整个脚本重跑一遍,观察是否报主键冲突,能理解这一步背后的“自增不回退”机制,你对IDENTITY的理解就彻底过关了。
人事管理系统用 SQL Server 做,价值不在于页面多花哨,而在于你能把关系模型、约束、查询、存储过程、事务这些概念装进一个可运行的系统里。我每次带学生做这个题目都要求他们把提交的文档里附上完整脚本和每个表的SELECT *结果截图,这一套做完,数据库课程设计的核心目标就达成了。希望帮到你。
本文还有配套的精品资源,点击获取