首页 / 资讯中心 / 文章详情

Python+MySQL员工信息管理系统:数据库课程设计完整实现指南

Python+MySQL员工信息管理系统:数据库课程设计完整实现指南 ★ FEATURED ARTICLE
简介这套员工信息管理系统源码包来自数据库课程设计基于Python和MySQL开发适合高校计算机专业学生在课程设计、毕业设计或实训项目中参考。资源共9个文件体积约6.71MB包含2个Python源程序、2份doc课程设计报告与说明文档、4张png设计图E-R图、数据流图及系统运行界面以及1个md说明文件类型覆盖代码、文档和图表便于对照学习。项目通过pymysql库连接MySQL实现了员工档案的新增、查询、修改、删除以及部门信息管理等典型功能完整演示了从数据库设计、SQL语句编写到Python业务逻辑实现的流程。文档和图片能帮助理解表结构、数据流向和界面设计源码则可以直接运行和扩展。已有4343人学习下载对于需要快速完成员工信息管理系统课设的读者这份资源提供了可复用的代码模板和清晰的文档思路能够节省大量从零搭建的时间。1. 为什么用 Python MySQL 做员工信息管理系统课设需求与技术选型“数据库课程设计”这个题目每年都有一批人卡壳不是不会写 SQL而是不知道系统要拆几个模块、数据落成几张表、代码长什么样才算完整。真正动手建库时很多人反而在第一行连接代码就卡住了。这套员工信息管理系统只用了 Python 和 MySQL 两样技术Python 负责业务逻辑与命令行交互MySQL 负责数据落地。Python 语法友好、单文件能跑通MySQL 是导师最熟悉的数据库看表结构就知道你在做什么是课设里最稳的组合。系统走控制台菜单交互不依赖前端框架把考察重点放在数据组织、SQL 编写和事务处理上。适合要交数据库课程设计的学生也适合想快速搭一套完整参考系统的开发者。下面一条线拆完照着改就能交。2. 需求分析与总体架构设计四大功能模块与五张核心表的职责划分课设和真实项目的差别在于范围要小但链条要完整。员工信息管理系统看起来简单真要覆盖“录入—维护—查询—统计”这条链路需求就得先拆清楚否则建表时就会漏字段写代码时又会发现功能互相打架。这一章先落需求清单再定架构最后把五张表的结构定死后续所有代码都围着这份设计走。2.1 需求拆解从课设任务书到可落地的功能清单任务书里常见的要求是“实现员工信息的录入、修改、删除、查询和统计”这句话只能算方向不能直接开工。我在拆需求时通常会把每个功能对应到具体的数据库操作确保每个菜单项都有明确的 SQL 支撑也方便答辩时回答“这个功能是怎么实现的”。功能模块子功能对应数据库操作登录鉴权管理员登录、退出、密码校验admin_user 表条件查询员工信息管理新增员工、修改信息、离职处理、查看详情employee 表增删改查查询统计多条件组合查询、分页浏览、部门人数统计SELECT WHERE / GROUP BY辅助管理部门维护、统计报表输出department 表维护、结果导出四个模块对应四个独立的代码文件菜单在入口文件里统一调度。逻辑上每个模块只做一件事登录模块只校验身份员工模块只处理单条员工数据的增删改统计模块只做聚合查询。这样拆的好处很明显答辩时导师问“模块化怎么体现”直接说文件划分和调用关系就能答清楚。2.2 总体架构展示层、业务层、数据层的三层划分整个系统按三层结构组织最上层是控制台菜单和输入输出中间层是业务逻辑最底层是数据库操作。三层之间不交叉展示层不会直接写 SQL业务层不负责打印菜单数据层只提供连接和执行查询的通道。文件职责main.py菜单循环、功能分发、登录状态控制config.py数据库连接参数集中管理db.py获取连接、关闭连接的公共工具auth.py登录鉴权、密码校验employee.py员工信息新增、修改、删除、查询stats.py多条件查询、分页、部门统计、报表输出调用关系是单向的main.py 根据用户输入调用 auth 或 employee 或 stats这三个模块通过 db.py 拿连接执行 SQL执行完把结果返回展示层。这里有个容易被忽略的点db.py 是唯一允许出现 pymysql 操作的地方其他模块只调用它提供的函数这样万一要换数据库或者改连接方式只动一个文件。2.3 数据库设计五张核心表的建表 SQL表结构直接决定代码好不好写也决定答辩时能不能讲出设计依据。这里设计了五张表admin_user 管登录department 管部门employee 存员工主体信息attendance 存考勤记录salary 存工资记录。核心是 employee 表另外四张围着它转。CREATE DATABASE IF NOT EXISTS employee_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE employee_db; CREATE TABLE admin_user ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(30) NOT NULL UNIQUE, password_hash CHAR(32) NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE department ( dept_id INT AUTO_INCREMENT PRIMARY KEY, dept_name VARCHAR(50) NOT NULL UNIQUE, manager VARCHAR(20), phone VARCHAR(20) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE employee ( emp_id INT AUTO_INCREMENT PRIMARY KEY, emp_no VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(20) NOT NULL, gender CHAR(2) DEFAULT 男, birthday DATE, phone VARCHAR(20), email VARCHAR(50), dept_id INT NOT NULL, hire_date DATE, salary DECIMAL(10,2) DEFAULT 0.00, status TINYINT DEFAULT 1 COMMENT 1在职 0离职, CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES department(dept_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE attendance ( att_id INT AUTO_INCREMENT PRIMARY KEY, emp_id INT NOT NULL, work_date DATE NOT NULL, check_in TIME, check_out TIME, status VARCHAR(10) DEFAULT 正常, CONSTRAINT fk_att_emp FOREIGN KEY (emp_id) REFERENCES employee(emp_id), UNIQUE KEY uk_emp_date (emp_id, work_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE salary ( sal_id INT AUTO_INCREMENT PRIMARY KEY, emp_id INT NOT NULL, month CHAR(7) NOT NULL COMMENT 格式 2025-01, base_salary DECIMAL(10,2) DEFAULT 0.00, bonus DECIMAL(10,2) DEFAULT 0.00, deduction DECIMAL(10,2) DEFAULT 0.00, final_salary DECIMAL(10,2) DEFAULT 0.00, CONSTRAINT fk_sal_emp FOREIGN KEY (emp_id) REFERENCES employee(emp_id), UNIQUE KEY uk_emp_month (emp_id, month) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;建表脚本一次性执行完初始化管理员账号和几个测试部门也放在同一个 SQL 文件里方便课程设计报告里附上“数据初始化脚本”这一节。注意所有表都指定了 ENGINEInnoDB因为 InnoDB 支持事务和外键这是答辩时一定会被问到的点。2.4 关键字段与约束设计说明这里几个设计决策是答辩的加分项。第一员工表用 emp_id 自增主键同时给 emp_no 加唯一约束工号是业务上唯一的标识主键是内部使用两者分离可以避免工号修改时牵连外键。第二工资字段用 DECIMAL(10,2) 而不是 FLOAT金额用浮点会累积精度误差这是数据库设计的经典考点。第三status 字段做软删除标记离职不物理删行保留历史数据。表间关系是典型的 1:N部门对员工一对多员工对考勤一对多员工对工资一对多。员工表里只存 dept_id 不冗余部门名称符合第三范式的要求这也是“为什么这样设计”的标准答案。考勤表的唯一键 (emp_id, work_date) 保证同一天不能重复打卡工资表的唯一键 (emp_id, month) 保证一个月只能有一条工资记录两个唯一键都是靠约束兜底比代码里先查后插更可靠。3. 核心模块代码实现连接封装、登录鉴权与员工 CRUD 落地设计稿定了就要落到代码。这一章按“连接工具 → 登录鉴权 → CRUD → 软删除”的顺序写每一段代码都是能直接复制到项目里跑通的重点不是炫技而是让课设代码看起来结构清晰、每行都有存在的理由。3.1 数据库连接模块配置集中管理与连接工具封装连接信息如果散落在各个文件里改一次密码要翻三个文件这是新手项目最常见的坏味道。做法是把连接参数统一放到 config.pydb.py 只负责“拿连接”和“关连接”两件事。# config.py DB_CONFIG { host: localhost, port: 3306, user: root, password: 123456, database: employee_db, charset: utf8mb4 }# db.py import pymysql from config import DB_CONFIG def get_conn(): return pymysql.connect( hostDB_CONFIG[host], portDB_CONFIG[port], userDB_CONFIG[user], passwordDB_CONFIG[password], databaseDB_CONFIG[database], charsetDB_CONFIG[charset], cursorclasspymysql.cursors.DictCursor, # 查询结果返回字典按列名取值 autocommitFalse # 关闭自动提交事务由业务代码控制 ) def close_all(conn, cursor): if cursor: cursor.close() if conn: conn.close()cursorclass 指定 DictCursor 后fetchone() 返回的是字典可以用 row[name] 取值比默认的元组下标好读得多。autocommitFalse 是刻意的插入、修改必须显式 commit 才生效配合异常分支的 rollback才能讲清楚事务处理。close_all 放在 finally 里调用保证连接一定会被释放这一条直接规避后面要讲的“连接泄漏”问题。3.2 登录鉴权模块密码哈希与登录状态控制登录模块的逻辑很简单输入用户名密码查 admin_user 表匹配成功就进入主菜单。但密码不能明文存储也不能用字符串拼接 SQL。下面的写法把密码做 MD5 哈希后落库查询全部走参数化。# auth.py import hashlib from db import get_conn, close_all def md5_password(pwd: str) - str: MD5 哈希用于密码存储和校验 return hashlib.md5(pwd.encode(utf-8)).hexdigest() def login(): conn get_conn() cursor conn.cursor() try: username input(请输入用户名: ).strip() password md5_password(input(请输入密码: ).strip()) sql SELECT id, username FROM admin_user WHERE username%s AND password%s cursor.execute(sql, (username, password)) user cursor.fetchone() if user: print(f登录成功欢迎 {user[username]}) return user print(用户名或密码错误) return None finally: close_all(conn, cursor)踩过的坑是新手容易把 MD5 写在 SELECT 外面然后和数据库里存的明文比两边永远对不上。正确口径是“注册时存哈希登录时算哈希再比哈希”。初始化管理员数据时用下面这句INSERT INTO admin_user (username, password_hash) VALUES (admin, MD5(123456))。md5 在真实生产环境已经不够安全课设里用它展示“密码不能明文落库”的思路即可代码注释里写明可换 bcrypt。3.3 员工新增与修改参数化 INSERT / UPDATE 的完整写法员工新增是最能体现基本功的功能字段多、类型杂、还要处理异常。下面这段把输入、校验、执行、提交、回滚串成一条完整链路。# employee.py from db import get_conn, close_all def add_employee(): conn get_conn() cursor conn.cursor() try: emp_no input(请输入工号: ).strip() name input(请输入姓名: ).strip() gender input(请输入性别(男/女): ).strip() dept_id int(input(请输入部门ID: ).strip()) salary float(input(请输入工资: ).strip()) sql INSERT INTO employee (emp_no, name, gender, dept_id, salary, status) VALUES (%s, %s, %s, %s, %s, 1) cursor.execute(sql, (emp_no, name, gender, dept_id, salary)) conn.commit() print(f员工 {name} 添加成功影响行数 {cursor.rowcount}) except ValueError: conn.rollback() print(输入格式有误部门ID和工资必须是数字) except Exception as e: conn.rollback() print(f添加失败{e}) finally: close_all(conn, cursor)注意两个细节所有用户输入都先 strip() 去掉头尾空格防止“工号带了空格导致唯一约束冲突”这种玄学问题int() 和 float() 包在 try 里用户输入非数字时不会让整个程序崩溃。修改员工用 UPDATE employee SET name%s, phone%s WHERE emp_id%s套路完全一样只多一个 WHERE 条件定位记录。提示execute 的第二个参数传元组哪怕只有一个占位符也要写成 (value, )逗号不能省这是 Python 语法里最容易漏的地方。3.4 离职处理用软删除代替物理删除删除功能如果用 DELETE员工历史记录就没了后面考勤和工资表的外键还会变成孤儿数据。更稳的做法是把 status 从 1 改成 0查询默认只看在职员工离职数据保留在库里。def remove_employee(emp_id: int): conn get_conn() cursor conn.cursor() try: sql UPDATE employee SET status0 WHERE emp_id%s AND status1 cursor.execute(sql, (emp_id,)) conn.commit() if cursor.rowcount 0: print(该员工不存在或已离职) else: print(f员工 {emp_id} 已标记为离职) except Exception as e: conn.rollback() print(f操作失败{e}) finally: close_all(conn, cursor)软删除在答辩时很好讲一是数据可追溯二是保留考勤工资历史三是只改一个字段成本低。对应的查询语句统一加上 AND status1列表页就不会出现已离职人员。4. 查询统计与报表输出多条件筛选、分页与部门人数统计的做法查询统计是课设里差异化最大的部分做得好的能直接把评分拉高。这一章解决三个实际问题条件可有可无时 SQL 怎么写、数据多了怎么分页、统计结果怎么让导师一眼看到亮点。4.1 多条件组合查询动态 SQL 与参数校验用户可能只输入姓名也可能同时输入部门加最低工资条件数量不确定。动态拼接 WHERE 子句的标准做法是维护一个参数列表只有用户输入了才追加条件。def search_employee(): conn get_conn() cursor conn.cursor() try: name input(姓名(直接回车跳过): ).strip() dept_id input(部门ID(直接回车跳过): ).strip() min_salary input(最低工资(直接回车跳过): ).strip() sql SELECT * FROM employee WHERE status1 params [] if name: sql AND name LIKE %s params.append(f%{name}%) if dept_id: sql AND dept_id %s params.append(int(dept_id)) if min_salary: sql AND salary %s params.append(float(min_salary)) cursor.execute(sql, params) rows cursor.fetchall() if not rows: print(没有匹配的记录) for r in rows: print(f{r[emp_no]} {r[name]} 部门{r[dept_id]} 工资{r[salary]}) finally: close_all(conn, cursor)WHERE status1 后面跟 AND 是常用技巧叫“拼条件前先放一个恒真条件”避免判断“是不是第一个条件要不要加 WHERE”的麻烦。更关键的是动态拼的是 SQL 结构值永远走 %s 参数化用户输入再花哨也不会被当成 SQL 执行。LIKE 查询记得自己拼好 % 再传给参数不要在 SQL 里写 LIKE %s%那会直接报错。4.2 分页查询LIMIT / OFFSET 与页码边界数据超过几十条后一次性查出来既不美观也不好讲解。分页查询要处理两件事算总页数、判断页码越界。def page_query(page: int, page_size: int 5): conn get_conn() cursor conn.cursor() try: cursor.execute(SELECT COUNT(*) AS cnt FROM employee WHERE status1) total cursor.fetchone()[cnt] total_pages (total page_size - 1) // page_size if page 1 or page total_pages: print(f页码越界共 {total_pages} 页) return sql SELECT emp_no, name, dept_id, salary FROM employee WHERE status1 ORDER BY emp_id LIMIT %s OFFSET %s cursor.execute(sql, (page_size, (page - 1) * page_size)) for r in cursor.fetchall(): print(f{r[emp_no]} {r[name]} 部门{r[dept_id]} 工资{r[salary]}) print(f第 {page}/{total_pages} 页共 {total} 条) finally: close_all(conn, cursor)总页数用 (total page_size - 1) // page_size 向上取整这是整数分页的标准写法。OFFSET 是 (page-1) * page_size第 1 页偏移 0第 2 页偏移 5这条公式背下来就不会错。判断越界放在查询之前否则用户输了个 999 页数据库白查一遍答辩时提一句“先算页数再决定要不要查”就能体现细节。4.3 部门人数统计GROUP BY 与左连接统计每个部门多少人核心是 GROUP BY但直接用员工表分组会漏掉没有人的空部门。要在答辩中讲出水平就得用 LEFT JOIN。def dept_stat(): conn get_conn() cursor conn.cursor() try: sql SELECT d.dept_name, COUNT(e.emp_id) AS emp_count FROM department d LEFT JOIN employee e ON d.dept_id e.dept_id AND e.status1 GROUP BY d.dept_id, d.dept_name ORDER BY emp_count DESC cursor.execute(sql) for r in cursor.fetchall(): print(f{r[dept_name]}: {r[emp_count]} 人) finally: close_all(conn, cursor)LEFT JOIN 保证 department 表每行都出现没人的部门 COUNT(e.emp_id) 结果是 0。注意 ON 条件里带了 e.status1把已离职的人排除在统计口径外。GROUP BY 的列要和 SELECT 里出现的非聚合列一致这里按 dept_id 和 dept_name 两个字段分组MySQL 的 ONLY_FULL_GROUP_BY 模式才不会报错。4.4 报表输出统计结果导出 CSV课设加分项是把统计结果落成文件导师不用看控制台滚动直接打开 Excel 就能检查。用 Python 标准库 csv 写文件二十行以内搞定。import csv def export_dept_stat(): conn get_conn() cursor conn.cursor() try: cursor.execute(SELECT d.dept_name, COUNT(e.emp_id) AS emp_count FROM department d LEFT JOIN employee e ON d.dept_id e.dept_id AND e.status1 GROUP BY d.dept_id, d.dept_name) rows cursor.fetchall() with open(dept_stat.csv, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([部门名称, 员工人数]) for r in rows: writer.writerow([r[dept_name], r[emp_count]]) print(已导出 dept_stat.csv) finally: close_all(conn, cursor)编码用 utf-8-sig 而不是 utf-8因为 Excel 打开 UTF-8 无 BOM 的 CSV 会乱码这是导出功能最容易翻车的点。csv.writer 配 newline 是 Python 官方推荐写法避免 Windows 下多出空行。这一小段代码放进报告里页面效果远好于贴一堆打印语句。5. 常见问题与避坑指南课设开发中最容易翻车的五个点下面五个问题是我见过出现频率最高的基本覆盖了从建库到交付的完整链路。每条都按“现象 → 原因 → 解决”写开发时踩到任何一个回来对照就能定位。5.1 中文乱码三处字符集不一致现象插入中文后查询显示成 ?? 或者一堆看不懂的符号控制台输出也被搅乱。原因字符集不一致。MySQL 服务端默认可能是 latin1建库时没指定字符集Python 连接也没传 charset三处各用各的编码中文在转换过程中直接丢失。很多同学只改一处问题依旧以为是玄学。解决建库时统一指定 utf8mb4连接参数里加 charsetutf8mb4如果还不行检查 my.ini 里 [mysqld] 段加 character-set-serverutf8mb4 并重启服务。判断问题在哪一处用命令行客户端插入一条中文再查能显示就是 Python 这边没配好不能显示就是库表字符集不对。5.2 连接泄漏Too many connections现象程序跑几次后突然报 pymysql.err.OperationalError: Too many connections重启 MySQL 才好。原因每次调用 get_conn() 都新建连接用完没关。MySQL 默认最多 151 个连接课设的循环菜单跑几十次就满了。这是新手最容易犯的错因为本地开发时前几次根本不报错问题一直积累到临界点才爆发。解决所有操作统一走 try/finallyfinally 里调 close_all(conn, cursor)。如果用的是 with conn.cursor() as cursor 写法也要确认连接本身被关闭。写代码时记住一条规则get_conn 和 close_all 必须成对出现就像开文件必须配 close 一样写完自查一遍。5.3 SQL 注入字符串拼接导致翻车现象在搜索框输入 OR 11查出全部员工数据甚至把表删了。原因SQL 用了 f-string 直接拼用户输入。select * from employee where name {name}这种写法用户输入一旦包含单引号SQL 语义就被改写条件恒真等于把数据库钥匙交出去了。课设里数据不重要但这个坏习惯会被导师直接点名。解决全部改用参数化查询占位符 %s值通过 cursor.execute(sql, params) 传入pymysql 会帮你做转义。注意表名、列名不能用占位符参数化但这类场景在课设里很少出现能用白名单校验的地方不要拼字符串。5.4 事务没提交数据改了查不到现象插入数据后程序显示“添加成功”但换个窗口查询没有这条记录或者程序重启后数据消失了。原因连接设置了 autocommitFalse执行 INSERT 后数据只存在当前事务里没有 commit 就不落盘。有的同学只在某些函数里写了 commit另一些函数漏了于是功能时好时坏非常误导排查方向。解决每个增删改操作成功路径上必须显式 commit异常路径上必须 rollback。建议在写操作函数的最后、打印成功信息之前调 commit固定位置写死别想着“最后统一提交”。还有一个后悔药实在不确定哪里漏了可以把 pymysql.connect 的 autocommit 改成 True 临时救急但答辩时要能说清两种模式的区别。注意fetchone() 返回 None 不代表出错要区分“没查到数据”和“SQL 执行异常”后者才需要进 except 分支。5.5 日期与金额字段的类型坑现象生日和入职日期显示成“1999/1/1 00:00:00”工资排序结果不对1 万和 9999 的排序经常出乎意料。原因建表时把生日建成 VARCHAR工资建成 FLOAT。字符串日期没法按大小比较浮点金额有精度误差排序和统计口径全是乱的。源头是建表时图省事没按语义选类型。解决日期字段用 DATE金额字段用 DECIMAL(10,2)。Python 这边写入日期时先处理成 YYYY-MM-DD 格式读取到的 datetime 对象用 strftime 再展示。工资初始化一律用 Decimal 或两位小数浮点不要从输入框直接拿原始字符串塞进数据库。6. 项目验收与答辩技巧从功能演示到提问预演代码写完不等于能交差课设的临门一脚是演示和答辩。我见过不少项目功能完整但演示时没有测试数据、操作顺序混乱导师看了十分钟还看不出系统做了什么。提前把流程和话术准备好效果是完全不同的。6.1 演示前必做的四步检查第一步重置数据库到干净状态重新执行建表和初始化脚本确保演示现场不会因为之前调试留下的脏数据翻车。第二步预置三到五条有辨识度的测试数据部门、工资、日期都要有点差异这样演示查询统计时能看到不同结果。第三步自己完整走一遍演示流程至少两遍重点验证增删改查后数据的一致性——新增后能查到离职后再查列表不出现。第四步准备一份“后悔药”脚本万一演示时误删了数据一键恢复测试数据不要现场手敲 SQL。6.2 答辩高频提问与参考答案提问方向建议回答要点为什么用 InnoDB支持事务、外键和行级锁适合需要保证数据一致性的业务为什么工号唯一还要自增主键主键供内部引用工号是业务标识两者解耦改工号不影响外键参数化查询怎么防注入值和 SQL 结构分开传输驱动对值做转义用户输入不会改变 SQL 语义GROUP BY 为什么带左连接内连接会漏掉空部门左连接保证每个部门都出现在结果里范式怎么体现员工表只存部门 ID不冗余部门名称避免修改部门名时多处更新答辩时被问住不用硬扛承认设计局限比狡辩强比如 MD5 的明文安全问题可以说“真实项目会换成 bcrypt课设重点是展示流程正确”导师反而认可你的知识面。6.3 收尾检查清单最后用十分钟核对四件事代码里不残留调试用的 print 和写死的路径配置文件里数据库密码改成通用占位说明报告里的表和实际建表语句完全一致源码文件里包含建表 SQL、初始化数据、核心代码三部分。我从那以后每次带课设项目都会强制走一遍“重置数据 → 完整演示 → 问题预演”的流程这个习惯帮我和学生避开了不少尴尬时刻也把答辩的主动权拿回自己手里。这套方案的完整源码、建表脚本和演示数据都是可以直接拿来改的按第二章的表结构建库再对照第三章和第四章的代码逐模块替换就能拼出你自己的员工信息管理系统。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站