ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

数据库课程设计实战:人事管理系统从E-R图到视图SQL全解析

数据库课程设计实战:人事管理系统从E-R图到视图SQL全解析 简介这是一份面向“数据库系统课程设计”的人事管理系统后台数据库设计报告适合高校计算机相关专业学生及需要完成数据库课设的开发者参考。文档以人事管理为业务背景完整覆盖需求分析、概念设计、逻辑设计、物理设计、数据库实施与功能实现并给出系统功能模块、数据字典、ER图转关系模型等内容可帮助读者理解从业务需求到数据库落地的全过程。压缩包内为单个doc文档大小1.23MB内含课程设计任务书、目录结构、数据项定义、表结构设计与统计查询方案。针对部门、员工、考勤、调动等核心场景报告详细说明了员工增删改查、模糊查询、按年月统计出勤、按部门统计迟到早退人数等功能的设计思路并附有参考书籍与排错经验可直接作为课程设计报告模板或SQL Server实践指导。目前已有1107人学习下载适合正在撰写数据库课程设计报告、需要完整案例支撑的同学使用。1. 数据库系统课程设计卡在哪一份从数据字典到视图SQL的全套人事管理设计写数据库课程设计多数人不是栽在SQL语法上而是需求分析这一页写不写得住、E-R图画出来经不经得起老师追问。这份人事管理系统的数据库系统课程设计刚好把整条链路补齐了任务书、数据字典、局部E-R图、全局E-R图一直串到六张表的逻辑结构最后还给了五个能直接跑的视图SQL。它解决的就是“从零开始交一份课设”的效率问题。适合正在写SQL Server课设的学生适合要给学生讲完整案例的助教也适合想半小时把建库、建表、视图这套流程过一遍的在职开发。文档不含任何安装包需要自己开一个SQL Server实例把语句一条条敲进查询窗口验证。下面按这份文档的骨架把每一步拆开讲重点放在可以直接抄、抄完不翻车的那部分。2. 需求分析与概念设计从数据字典到六张关系模式2.1 数据字典是第一道关先从需求里抠数据项人事管理课设需求通常会写得像一句话“实现信息自动化管理、支持模糊查询、按月统计出勤”。单靠这句话没法建表必须先拆数据项。这份文档里的数据字典列了接近三十个数据项覆盖员工、部门、考勤、工资、调动五大类。我的习惯是先按“人、部门、事件”分类再决定哪些是字段、哪些要单独成表员工类员工编号、姓名、性别、年龄、身份证号、入职时间、联系电话、所属部门、职称、工龄、基本工资。 部门类部门编号、部门名称、部门电话、部门经理。 事件类缺勤、迟到、早退、日期考勤事件底薪、补贴、奖金、扣款、加班费、实发工资工资事件原部门编号、新部门编号、调离时间、调入时间调动事件。这个分类看起来很朴素但它决定了后面E-R图的边界。常见做法是先画局部E-R图把员工、部门、考勤、工资、调动六个局部图分别画完再合并成全局E-R图并做一次优化。合并时最常遇到的问题就是同一个实体在多个局部图里出现比如员工既在基本信息里、又在工资和考勤里合并时把属性全部汇集到员工实体下再把联系连到部门和调动事件上。对应到功能模块文档归纳得比较完整基本信息、工作信息、部门信息、考勤信息、工资信息、调动信息、查询统计。前六张表各管一块查询统计是落在视图和SQL上的不单独建表。功能模块与表的对应关系如下功能模块负责的数据落地表基本信息模块员工档案staff_info工作信息模块职称、工龄、当前岗位staff_work部门信息模块部门档案与经理department_info考勤信息模块缺勤、迟到、早退记录staff_signin工资信息模块工资构成与实发额staff_salary调动信息模块跨部门调动历史staff_transfer查询统计模块各类查询与月/年统计视图 分组查询2.2 全局E-R图转关系模式三种联系三种落表规则概念设计产出的是E-R图逻辑设计要把它转成关系模式。四个基础规则可以直接背实体转成一个关系模式实体的属性就是关系的属性实体的码就是关系的码1:1联系并入任意一端实体表或单独转表1:n联系并入n端n端加一端的主键当外键m:n联系必须单独转一张中间表中间表主键是两端实体的主键组合。对应到人事场景部门与员工是1:n关系员工表或调动表上要保留部门编号部门经理与部门是1:1关系文档选择把经理姓名直接放部门表员工与工资、员工与考勤都是1:1或1:n关系直接用员工编号做主键关联。m:n关系在这个系统里并不明显真正接近m:n逻辑的是“员工-部门-时间”的调动事件员工在时间轴上和多个部门有关联文档把它处理成员工调动信息表这也是一种变通的落表方式。转换后文档给出的六组关系模式很清晰员工基本信息员工编号、姓名、性别、年龄、入职时间、所属部门、联系电话、身份证号、基本工资 员工工作信息员工编号、所属部门编号、职称、工龄 部门信息部门编号、部门名称、部门经理、部门电话 员工工资信息员工编号、底薪、补贴、奖金、扣款、加班费、实发工资 员工考勤信息员工编号、缺勤、迟到、早退、日期 员工调动信息员工编号、姓名、原部门编号、新部门编号、调离时间、调入时间粗看会觉得员工基本信息里已有“所属部门”调动表里还保留“原部门编号/新部门编号”是不是重复其实这两条线目的不同基本信息表里是当前部门用于日常展示调动表是历史流水用于统计年内调入调出人数。当前部门一旦变更准确做法是往调动表插一条记录由新部门编号反推当前部门。这个取舍在视图章节会体现得很明显。2.3 逻辑设计补一步把关系模式量化为表结构E-R图转出关系模式只是第一步后面还要对每个关系模式做规范化检查。这份文档的表结构基本满足第三范式工资表的主键是员工编号不依赖员工姓名考勤表能拆出员工编号和日期两个独立维度调动表把原部门和目标部门拆成两个编号字段避免把一段调动历史塞进一个字符串字段里。检查要点就三条主键是否每行唯一、非主属性是否完全依赖主键、是否存在传递依赖。这个阶段最值得花时间的其实是给每个字段定长度和约束因为建表SQL里的NOT NULL、UNIQUE、CHECK都从这里来。比如身份证号定CHAR(18)性别可以加CHECK约束限制为男或女年龄加范围约束。把这些在逻辑设计阶段写清楚下一步物理设计就只是照抄。3. 逻辑设计与物理设计在SQL Server里把六张表建出来3.1 物理设计先决策两件事文件组和主键策略物理设计要回答“数据在磁盘上怎么放、查询怎么走”。文档里采用了一个主数据文件、一个次要数据文件和一个事务日志文件的组合意图是把数据和日志分开、把数据横跨到多块物理磁盘。实际课设环境一般只有一块盘物理分层意义不大但日志文件独立是必须的数据库恢复、误删后悔药都靠它。CREATE DATABASE语句可以先准备这么一段USE master; GO IF DB_ID(HRMS) IS NOT NULL BEGIN ALTER DATABASE HRMS SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE HRMS; END GO CREATE DATABASE HRMS ON PRIMARY ( NAME NHRMS, FILENAME ND:\SQLData\HRMS.mdf, SIZE 5MB, MAXSIZE UNLIMITED, FILEGROWTH 1MB ), ( NAME NHRMS_Data2, FILENAME ND:\SQLData\HRMS_Data2.ndf, SIZE 5MB, MAXSIZE UNLIMITED, FILEGROWTH 1MB ) LOG ON ( NAME NHRMS_Log, FILENAME ND:\SQLData\HRMS_Log.ldf, SIZE 2MB, MAXSIZE UNLIMITED, FILEGROWTH 10% ); GO注意FILENAME里的D:\SQLData要换成你本机SQL Server的数据目录路径不存在建库会直接失败。这段脚本还带了IF DB_ID判空逻辑目的是让脚本可以重复执行不会第二次跑就报“数据库已存在”。主键策略上员工编号这类业务编号不是越长越稳我一般用IDENTITY自增主键把员工编号交给数据库生成避免并发插入时人工编号重复。部门编号同理。3.2 六张核心表的CREATE TABLE完整语句表结构按文档的关系模式来建表SQL如下顺序按外键依赖从主到从。先建部门表让后面员工工作表的部门编号能引用CREATE TABLE department_info ( department_id INT IDENTITY(1,1) PRIMARY KEY, department_name NVARCHAR(40) NOT NULL UNIQUE, department_phone VARCHAR(15) NOT NULL, manager_name NVARCHAR(20) NOT NULL );部门名称加了UNIQUE约束避免同一部门重复录入manager_name存的是经理姓名按文档的做法存文本不设外键因为经理本身也是员工真要严格可以拆一个manager_id出来但课设场景存姓名更直接。员工档案表CREATE TABLE staff_info ( staff_id INT IDENTITY(1,1) PRIMARY KEY, name NVARCHAR(20) NOT NULL, gender NVARCHAR(2) NOT NULL CHECK (gender IN (N男, N女)), age INT CHECK (age BETWEEN 18 AND 60), id_card CHAR(18) NOT NULL UNIQUE, hire_date DATE NOT NULL, department NVARCHAR(40) NOT NULL, phone VARCHAR(15) NOT NULL DEFAULT N未登记 );staff_info里的department是当前部门的冗余展示字段真正严谨的当前部门判断要交给调动表。gender和age的CHECK约束在课设答辩时是加分项能体现做完整性设计的意识。员工工作信息表与员工表一对一CREATE TABLE staff_work ( staff_id INT PRIMARY KEY, department_id INT NOT NULL, position NVARCHAR(20), work_age TINYINT, FOREIGN KEY (staff_id) REFERENCES staff_info(staff_id), FOREIGN KEY (department_id) REFERENCES department_info(department_id) );这里work_age用TINYINT上限255工龄基本够用。department_id按文档设计保留并补上了外键避免员工被分到一个不存在的部门。工资表、考勤表、调动表CREATE TABLE staff_salary ( staff_id INT PRIMARY KEY, basic_salary DECIMAL(10,2) NOT NULL, subsidy DECIMAL(10,2) DEFAULT 0, bonus DECIMAL(10,2) DEFAULT 0, deduction DECIMAL(10,2) DEFAULT 0, overtime_pay DECIMAL(10,2) DEFAULT 0, real_salary DECIMAL(10,2) NOT NULL, FOREIGN KEY (staff_id) REFERENCES staff_info(staff_id) );CREATE TABLE staff_signin ( id INT IDENTITY(1,1) PRIMARY KEY, staff_id INT NOT NULL, absent BIT DEFAULT 0, late BIT DEFAULT 0, leave_early BIT DEFAULT 0, signin_date DATE NOT NULL, FOREIGN KEY (staff_id) REFERENCES staff_info(staff_id) );CREATE TABLE staff_transfer ( id INT IDENTITY(1,1) PRIMARY KEY, staff_id INT NOT NULL, name NVARCHAR(20) NOT NULL, old_dep_id INT, new_dep_id INT, old_time DATE, new_time DATE, FOREIGN KEY (staff_id) REFERENCES staff_info(staff_id) );考勤记录是逐天的所以必须用id自增主键不能拿员工编号当主键调动表同理一个人调三次就是三条记录。提示创建表时如果报“对象已存在”说明库里已有同名表在脚本开头加上IF OBJECT_ID(dbo.staff_info, U) IS NOT NULL DROP TABLE dbo.staff_info; 即可重复执行。3.3 字段类型选型的四个坑文档数据字典里部分字段类型写得比较随意真正落地时要改。第一考勤和调动的时间字段用DATE而不是DATETIME迟到早退统计只精确到日DATE少占一半空间而且GROUP BY月份时直接YEAR/MONTH取值更干净。第二金额字段从FLOAT改成DECIMAL(10,2)float存金额会在求和时出现0.10.20.30000000000000004这类玄学误差课设里把实发工资算错是最尴尬的。第三中文文本用NVARCHAR而不用VARCHARvarchar在中文环境下按字节存储遇到不同排序规则容易变成乱码。第四布尔态字段用BIT而不是INT存0/1考勤表里缺勤/迟到/早退三个标记位用BIT更省、语义也更清楚。字段命名的习惯也在这里定下来name、time、date这类模糊词能不用就不用。staff_transfer里的name建议改成employee_name考勤表里的时间字段直接叫signin_date后面写视图时能省掉一堆引号转义的麻烦。4. 功能实现五个视图的SQL写法与翻译逻辑4.1 视图一员工个人信息视图一条链路串四张表视图要保证最终呈现的逻辑像一张宽表不给前端暴露真实的联合查询。员工个人信息视图把department_info、staff_transfer、staff_info和staff_work串起来DROP VIEW IF EXISTS v_staff_personal_info; GO CREATE VIEW v_staff_personal_info AS SELECT si.staff_id AS 员工编号, si.name AS 姓名, di.department_id AS 部门编号, di.department_name AS 部门名称, sw.position AS 职位, sw.work_age AS 工龄 FROM department_info di INNER JOIN staff_transfer st ON di.department_id st.new_dep_id INNER JOIN staff_info si ON st.staff_id si.staff_id INNER JOIN staff_work sw ON si.staff_id sw.staff_id;逻辑说明通过staff_transfer的new_dep_id关联department_info取员工当前所在部门再通过staff_id把员工档案和工作信息补上来。这个视图成立的前提是入职时也要往staff_transfer插入一条记录否则调动表里没有该员工JOIN不出来。如果发现视图里人数比员工总数少先查staff_transfer是否漏了记录——这是视图里比较隐蔽的坑。另一个设计点是视图用INNER JOIN员工必须同时有调动记录和工作记录才会出现在人事系统里这个约束成立因为入职能进系统就一定有调动流水。4.2 视图二三四部门员工、工资、考勤视图部门员工工作信息视图重点展示部门维度下所有员工DROP VIEW IF EXISTS v_department_staff; GO CREATE VIEW v_department_staff AS SELECT di.department_id AS 部门编号, di.department_name AS 部门名称, si.staff_id AS 员工编号, si.name AS 姓名, sw.position AS 职称, sw.work_age AS 工龄 FROM department_info di INNER JOIN staff_transfer st ON di.department_id st.new_dep_id INNER JOIN staff_info si ON st.staff_id si.staff_id INNER JOIN staff_work sw ON si.staff_id sw.staff_id;与视图一唯一的差别是少选了员工信息里的部门编号但多了一个部门维度的层级展示。设计上它对应“管理者进入系统后能看到公司管理系统”的需求支撑部门列表页使用。工资和考勤视图更直接DROP VIEW IF EXISTS v_staff_salary; GO CREATE VIEW v_staff_salary AS SELECT si.staff_id AS 员工编号, si.name AS 姓名, ss.basic_salary AS 底薪, ss.subsidy AS 补贴, ss.bonus AS 奖金, ss.deduction AS 扣款, ss.overtime_pay AS 加班费, ss.real_salary AS 实发工资 FROM staff_info si INNER JOIN staff_salary ss ON si.staff_id ss.staff_id;DROP VIEW IF EXISTS v_staff_signin; GO CREATE VIEW v_staff_signin AS SELECT si.staff_id AS 员工编号, si.name AS 姓名, sg.absent AS 缺勤, sg.late AS 迟到, sg.leave_early AS 早退, sg.signin_date AS 日期 FROM staff_info si INNER JOIN staff_signin sg ON si.staff_id sg.staff_id;工资视图把六项工资构成全部暴露给上层考勤视图把三个布尔标记位直接翻译成可读的缺勤/迟到/早退。这两个视图的JOIN都是单表连接字段语义清晰后面做统计时可以直接以视图为基础再套一层GROUP BY。原文档用的是中文视图名如“员工个人信息图”“员工工资信息”等中文视图名在SQL Server里合法可以保留我改成了v_前缀的英文命名纯粹是为了脚本批量管理时不用切换输入法语义对应关系在注释里写明即可。4.3 视图五调动视图原文档里最容易翻车的JOIN文档里的调动视图我直接跑过一次发现一个明显问题原部门名称和新部门名称取自同一次department_info关联结果“原部门名称”列显示的一直是新部门名称。正确做法是把部门表JOIN两次分别起别名DROP VIEW IF EXISTS v_staff_transfer; GO CREATE VIEW v_staff_transfer AS SELECT si.name AS 职工姓名, st.old_dep_id AS 原部门编号, do.department_name AS 原部门名称, st.old_time AS 调离时间, st.new_dep_id AS 新部门编号, dn.department_name AS 新部门名称, st.new_time AS 调入时间 FROM staff_info si INNER JOIN staff_transfer st ON si.staff_id st.staff_id LEFT JOIN department_info do ON st.old_dep_id do.department_id LEFT JOIN department_info dn ON st.new_dep_id dn.department_id;这里用LEFT JOIN而不是INNER JOIN是因为员工离职后原部门可能已经被删掉LEFT JOIN能保证员工调动记录还在部门名称显示NULL而不是整行消失。逻辑说明两个LEFT JOIN各管一个方向原部门走do的别名新部门走dn的别名如果有人调出后原部门被删LEFT JOIN能兜住边界这是调动历史类视图的通用写法。4.4 中文列名与保留字跑视图前先看这四个语法点文档里视图列名用的中文在SQL Server里完全合法但几个细节特别容易踩。第一CREATE VIEW之前必须先DROP VIEW IF EXISTS否则第二次执行直接报“对象已存在”。文档每个视图开头都有这行属于好习惯我保留了。第二SQL文本里不要出现中文单引号、中文括号。原文档里有多处中文引号直接复制进SSMS会报语法错误需要全部替换成英文引号。第三name、time、date这类字段名虽然SQL Server允许但建议改名调动表里的name改成employee_name考勤表里的时间字段改成signin_date避免应用层拼接SQL时踩保留字。第四视图里使用中文别名时如果别名里带空格要加[]或双引号写成AS [员工编号] 或不用空格直接AS 员工编号。5. 避坑实录字段类型、日期统计与视图设计的五个坑5.1 五个典型的翻车现场现象原因解决视图第二次执行报“对象已存在”没先DROP视图视图脚本前加DROP VIEW IF EXISTSvarchar中文变乱码排序规则或长度计算问题用NVARCHAR字符串常量前加N工资求和出现长尾小数用float存十进制金额改DECIMAL(10,2)金额统一两位小数考勤统计人数翻倍多表JOIN产生行数放大先按员工聚合再JOIN部门调动视图原部门名称变成新部门名称department_info只JOIN了一次两次LEFT JOIN分别起别名展开说。第一个坑在4.4已经讲过属于视图脚本的通病。第二个坑的关键点在字符串常量比如gender 男如果库的排序规则不是中文优先varchar里存中文容易出问号写成N男、字段用NVARCHAR基本能根除。第三个坑是float的二进制浮点表示导致的不是SQL Server特例任何语言都这样金额字段统一DECIMAL(10,2)实发工资、扣款、加班费全部按这个标准来。第四个坑比较隐蔽。统计某部门迟到早退人数时如果直接staff_info JOIN staff_signin再JOIN department_info一个员工30天有30条考勤记录再叠加部门和员工表的连接行数会被自然放大。解决思路是先把考勤表按员工和日期聚合成一行汇总再JOIN部门和员工表保证统计口径是“人数”而不是“记录数”。第五个坑就是4.3的调动视图原部门和新部门如果共用一次JOINSQL不会报错但结果一定错这种错不跑数据根本发现不了。5.2 出勤和调动统计的三条SQL写法需求分析里那句“按年份月份统计某个职工出勤、按某日期统计某部门迟到早退人数、按年统计各部门调入调出人数”落地成SQL是这样SELECT YEAR(signin_date) AS 统计年, MONTH(signin_date) AS 统计月, SUM(CASE WHEN absent 1 THEN 1 ELSE 0 END) AS 缺勤天数, SUM(CASE WHEN late 1 THEN 1 ELSE 0 END) AS 迟到次数, SUM(CASE WHEN leave_early 1 THEN 1 ELSE 0 END) AS 早退次数 FROM staff_signin WHERE staff_id 1001 GROUP BY YEAR(signin_date), MONTH(signin_date) ORDER BY 统计年, 统计月;逻辑说明先按员工编号过滤再按年月分组三个SUM分别汇总三个状态。参数说明CASE WHEN里判断的是BIT类型直接用1比较即可。部门维度迟到早退人数写法上要特别注意不要按5.1第四坑的方式JOINSELECT di.department_name AS 部门名称, sg.signin_date AS 日期, SUM(CAST(sg.late AS INT)) AS 迟到人数, SUM(CAST(sg.leave_early AS INT)) AS 早退人数 FROM staff_signin sg INNER JOIN staff_work sw ON sg.staff_id sw.staff_id INNER JOIN department_info di ON sw.department_id di.department_id WHERE sg.signin_date 2024-06-18 GROUP BY di.department_name, sg.signin_date;这里通过staff_work.department_id取部门而不是走staff_transfer因为staff_work更稳定调动表记录的是流动瞬间员工可能有多条new_dep_id历史记录直接JOIN调动表会把统计行数放大。调动统计SELECT YEAR(st.old_time) AS 年份, dn.department_name AS 部门名称, COUNT(*) AS 调出人数 FROM staff_transfer st LEFT JOIN department_info dn ON st.old_dep_id dn.department_id WHERE st.old_time IS NOT NULL GROUP BY YEAR(st.old_time), dn.department_name;调出和调入各写一条区别只在JOIN老部门还是新部门、过滤old_time还是new_time。三条统计SQL跑顺之后建议把索引补上CREATE INDEX ix_signin_staff_date ON staff_signin(staff_id, signin_date); CREATE INDEX ix_transfer_staff_time ON staff_transfer(staff_id, old_time, new_time); CREATE INDEX ix_work_department ON staff_work(department_id);索引说明staff_signin的索引把staff_id和signin_date排在一起月度统计走索引基本不回表staff_work的department_id是JOIN高频列建索引能避免嵌套循环全表扫描。数据库课设答辩时能讲清这三条索引的用途和只说“我建了索引”是两个评分档位。6. 进阶用法验收五连和视图权限设计拿到这套表设计第一件事不是急着往里面灌数据而是用一套验收脚本确认结构没建崩。我一般按五步检查查表和视图的清单、插一条非法数据看约束、连续执行视图脚本、核对排序规则、最后跑一次统计查询。-- 1 检查实际创建的表和视图 SELECT name, type_desc FROM sys.objects WHERE type IN (U, V) ORDER BY type, name; -- 2 故意插一条员工编号为9999的考勤打卡记录验证外键约束 INSERT INTO staff_signin (staff_id, signin_date) VALUES (9999, GETDATE());第二条会直接报主外键冲突说明参照完整性生效。如果没报错说明外键根本没建成返回去重新建表。-- 3 连续执行两次某视图的建视图语句确认IF EXISTS逻辑生效 -- 4 核对中文是否乱码 SELECT name, collation_name FROM sys.databases WHERE name HRMS; -- 5 跑一条月度出勤统计确认分组和聚合结果符合预期这五步跑完表结构基本就是稳的。再进一步是给视图加权限不要把基表直接丢给应用层CREATE USER app_user WITHOUT LOGIN; GRANT SELECT ON v_staff_personal_info TO app_user; GRANT SELECT ON v_staff_signin TO app_user;把SELECT权限收在视图层应用账号只能看到业务视图基表结构对外是黑匣子。这一招在课程设计里未必是硬性要求但能在答辩时讲出“视图不只是简化查询还是权限边界”的设计理由档次会不一样。回看这套人事管理课程设计最值的不是文档里的SQL本身而是它把需求分析、ER图、表结构、视图写的整条链路都走了一遍。我第一次照它的调动视图抄查了半天才发现原部门名称跟着新部门跑后来凡是看到视图里信息对不上第一反应就是去查JOIN方向而不是怀疑字段取值。从那以后我每次建视图都会强制自己检查一遍两个LEFT JOIN的别名方向写清楚再执行。希望帮到你。本文还有配套的精品资源点击获取
返回列表