简介这份资源是面向高校数据库课程设计场景的完整项目源码适合正在完成课程作业或希望练习 Python 与 MySQL 交互的初学者。项目以员工信息管理系统为主题覆盖数据库设计、SQL 增删改查、事务处理、用户交互与异常处理等核心知识点可作为课程设计参考或自学练手案例。压缩包共 9 个文件包含 2 个 Python 源码文件、2 个 Word 课程设计报告、4 张系统截图以及 1 个 README 说明文档整体约 6.71MB源码与文档配套齐全便于对照理解实现思路。目前已有 4343 人学习下载说明该案例在同类课程设计中具有较高参考价值。读者可从中获取可运行的员工管理程序、E-R 图与数据流图、课程设计报告模板以及数据库连接与操作示例快速搭建自己的课程设计框架并查漏补缺。1. 从一份员工信息管理系统源码说起课程设计怎么做出生产味很多同学拿到「数据库课程设计-员工信息管理系统源码(基于Python和MySQL实现」这个题目第一反应是去搜免费python源码大全找一份能跑的代码交差。但真正答辩时被问「你的数据库连接池怎么配的」「并发删除员工时事务怎么保证」就答不上来了。这份源码的价值不在于交作业而在于它是你第一次把 Python 语法、MySQL 增删改查、事务和索引串成一个完整系统的机会。我见过太多课程设计只做了单表 CRUD连外键约束都没开。这篇笔记按真实落地路径拆环境怎么搭、表怎么设计、代码怎么分层、坑在哪。适合正在做数据库课程设计、想拿高分或想真正理解 PythonMySQL 配合的从业者和学生。2. 环境搭建与项目骨架Python 和 MySQL 怎么装才不翻车2.1 Python 安装与环境隔离的实操步骤课程设计翻车最高频的地方不是代码是环境。很多人 python 安装教程看了一半就下最新版结果 MySQL 驱动装不上。我一般锁定 Python 3.10 或 3.11这两个版本对 mysql-connector-python 和 PyMySQL 兼容性最稳。安装时务必勾选 Add Python to PATH否则后面 vscode python 环境配置会找不到解释器。装完先验证再建虚拟环境。虚拟环境是后悔药装崩了直接删目录重来不污染全局。# 验证 Python 版本必须是 3.10 或 3.11 python --version # 在项目根目录创建虚拟环境目录名统一用 venv python -m venv venv # Windows 激活 venv\Scripts\activate # macOS / Linux 激活 source venv/bin/activate # 激活后命令行前缀应出现 (venv)再装依赖 pip install pymysql cryptography逻辑说明venv 把项目依赖和系统 Python 隔离cryptography 是 PyMySQL 连接 MySQL 8 默认加密认证方式时必须的包漏装会报 authentication plugin 错误。参数上Python 版本不要用 3.12部分驱动轮子还没跟上课程设计阶段没必要冒险。2.2 MySQL 安装配置与建库建表mysql 安装配置教程网上很多但课程设计只需要记住三件事字符集用 utf8mb4、认证方式选 mysql_native_password 或确认驱动支持 caching_sha2、root 密码别设成 123456 还提交到代码里。Linux 安装 mysql 和 Windows 安装器流程不同但建库语句一致。-- 建库字符集必须 utf8mb4否则中文姓名和 emoji 会乱码 CREATE DATABASE employee_ms DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; USE employee_ms; -- 部门表作为员工表的外键来源 CREATE TABLE department ( dept_id INT PRIMARY KEY AUTO_INCREMENT, dept_name VARCHAR(50) NOT NULL UNIQUE, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB; -- 员工表注意外键和索引 CREATE TABLE employee ( emp_id INT PRIMARY KEY AUTO_INCREMENT, emp_name VARCHAR(30) NOT NULL, gender TINYINT DEFAULT 0 COMMENT 0未知1男2女, salary DECIMAL(10,2) DEFAULT 0.00, dept_id INT, hire_date DATE, status TINYINT DEFAULT 1 COMMENT 1在职0离职, CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES department(dept_id) ON DELETE SET NULL, INDEX idx_dept (dept_id), INDEX idx_name (emp_name) ) ENGINEInnoDB;逻辑说明引擎必须 InnoDBMyISAM 不支持外键和事务课程设计答辩问事务直接挂。salary 用 DECIMAL 不用 FLOAT浮点算工资会有精度误差。dept_id 上建索引是因为按部门查员工是最高频查询。ON DELETE SET NULL 保证删部门不连带删员工比级联删除安全。mysql 设置默认值为 0 这类需求用 DEFAULT 0.00 直接写在列定义里即可。2.3 项目目录分层与连接配置别把所有代码堆一个文件。我一般分四层config 放连接配置dao 放 SQL 操作service 放业务逻辑ui 放界面或命令行入口。连接配置单独抽出来方便切换本地和测试库。# config/db_config.py DB_CONFIG { host: 127.0.0.1, port: 3306, user: root, password: your_password, # 不要硬编码提交到仓库 database: employee_ms, charset: utf8mb4, cursorclass: DictCursor # 返回字典取值更直观 }逻辑说明把配置独立成模块dao 层 import 即可。cursorclass 用 DictCursor 后查询结果直接是字典比元组下标取值可读性强得多。密码建议用环境变量读取课程设计至少做到不写死在业务代码里。3. 增删改查落地DAO 层怎么写才经得起答辩追问3.1 连接管理与数据库连接池的取舍新手最容易犯的错是每次操作都新建连接、用完不关跑几十次就报 too many connections。课程设计规模小用「每次操作开连接、with 上下文自动关」就够。但如果答辩老师问 mysql 的数据库连接池你要能说清楚连接池预先建好一批连接复用避免频繁握手开销生产用 DBUtils 或 SQLAlchemy 的 pool。课程设计里我一般手写一个极简连接工厂既体现理解又不引入过重依赖。# dao/db_helper.py import pymysql from config.db_config import DB_CONFIG def get_conn(): 每次调用返回一个新连接配合 with 使用自动关闭 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 query_all(sql, argsNone): with get_conn() as conn: with conn.cursor() as cur: cur.execute(sql, args or ()) return cur.fetchall()逻辑说明autocommitFalse 是事务控制的前提写操作后必须显式 commit。with 语句保证异常时连接也能释放。query_all 用参数化 args 传参杜绝 SQL 注入这是答辩必问点。参数 args 用元组或列表PyMySQL 会自动转义。3.2 员工增删改查的完整实现把 mysql update 语法、mysql 排序这些零散知识点收进一个 dao 类里。查询支持按姓名模糊、按部门过滤、按薪资排序这几个是课程设计演示的标配。# dao/employee_dao.py from dao.db_helper import get_conn, query_all class EmployeeDao: def add(self, name, gender, salary, dept_id, hire_date): sql INSERT INTO employee (emp_name, gender, salary, dept_id, hire_date) VALUES (%s, %s, %s, %s, %s) with get_conn() as conn: with conn.cursor() as cur: cur.execute(sql, (name, gender, salary, dept_id, hire_date)) conn.commit() # 写操作必须提交 return cur.lastrowid # 返回自增主键 def update_salary(self, emp_id, new_salary): sql UPDATE employee SET salary%s WHERE emp_id%s with get_conn() as conn: with conn.cursor() as cur: rows cur.execute(sql, (new_salary, emp_id)) conn.commit() return rows # 受影响行数0 表示 id 不存在 def delete(self, emp_id): sql DELETE FROM employee WHERE emp_id%s with get_conn() as conn: with conn.cursor() as cur: rows cur.execute(sql, (emp_id,)) conn.commit() return rows def search(self, keywordNone, dept_idNone, order_byemp_id): # order_by 白名单校验防止拼接注入 allowed {emp_id, salary, hire_date} if order_by not in allowed: order_by emp_id sql SELECT * FROM employee WHERE 11 args [] if keyword: sql AND emp_name LIKE %s args.append(f%{keyword}%) if dept_id: sql AND dept_id%s args.append(dept_id) sql f ORDER BY {order_by} DESC return query_all(sql, args)逻辑说明update 和 delete 返回受影响行数业务层据此判断操作是否命中记录比盲目提示成功靠谱。search 里 order_by 不能直接拼用户输入用白名单集合校验这是排序场景最容易被忽略的注入点。LIKE 的百分号拼在参数值里而不是 SQL 字符串里保持参数化。3.3 事务调岗调薪必须一起成功员工调部门同时调薪两条 UPDATE 必须在一个事务里否则第一条成功第二条失败数据就脏了。这是课程设计从「能跑」到「专业」的分水岭。# service/employee_service.py from dao.db_helper import get_conn def transfer_and_raise(emp_id, new_dept_id, new_salary): conn get_conn() try: with conn.cursor() as cur: cur.execute( UPDATE employee SET dept_id%s WHERE emp_id%s, (new_dept_id, emp_id)) cur.execute( UPDATE employee SET salary%s WHERE emp_id%s, (new_salary, emp_id)) conn.commit() # 两条都成功才提交 return True except Exception as e: conn.rollback() # 任一步失败全部回滚 print(事务回滚:, e) return False finally: conn.close() # 手动开的连接手动关逻辑说明这里没用 with get_conn()因为要跨多条语句共享同一连接和事务必须手动管理。commit 放在两条 execute 之后rollback 放在 except 里。finally 关连接防止泄漏。答辩问「并发下两个事务同时改同一员工会怎样」答 InnoDB 行锁会串行化后到的事务等待这就是选 InnoDB 的理由。4. 避坑与排查课程设计里最常翻车的五个点4.1 连接报错 error 2002 与 socket 问题现象运行时报 error 2002 (hy000): cant connect to local mysql server through socket /tmp/mysql.sock。原因客户端走 socket 连接但服务没起或 host 写成 localhost 时驱动优先走 socket 而 socket 路径不对。解决先确认 MySQL 服务已启动再把连接 host 从 localhost 改成 127.0.0.1 强制走 TCP端口确认是 3306。Linux 上用 systemctl status mysql 看服务状态。4.2 中文乱码从建库到连接的四道关现象插入的中文姓名查出来是问号或乱码。原因建库字符集、表字符集、连接 charset、Python 文件编码四处任一不是 utf8mb4 都会乱。解决建库建表显式写 utf8mb4连接配置 charset 写 utf8mb4Python 文件头声明 # -- coding: utf-8 --。四道关全对齐才彻底解决只改一处往往还乱。4.3 外键约束导致删除失败现象删部门时报 Cannot delete or update a parent row。原因员工表还有记录引用该部门外键挡住了。解决要么先把该部门员工 dept_id 置空或转移要么建表时用 ON DELETE SET NULL 让数据库自动处理。课程设计演示删除功能时先删子表再删主表是稳妥顺序。4.4 忘记 commit 导致数据「没存进去」现象程序提示新增成功重新查询却没有这条记录。原因autocommitFalse 时没调 conn.commit()连接关闭时未提交事务被回滚。解决所有写操作 execute 之后必须 commit或者把 autocommit 设 True但那样就丢了事务控制。我一般保持手动 commit逼自己养成习惯。4.5 排序字段拼接引发注入现象按薪资排序功能被输入特殊字符后报 SQL 语法错误甚至数据泄露。原因ORDER BY 后面直接拼接了用户输入参数化占位符对 ORDER BY 字段名不生效。解决用白名单集合校验排序字段不在集合里就回退默认字段绝不直接拼接。5. 进阶技巧用存储过程和索引把查询性能压下来课程设计做到能跑只是及格想拿高分得展示性能意识。员工表数据量上千后按部门加姓名组合查询会明显变慢这时候两个手段见效最快复合索引和存储过程。先说索引。前面单列索引 idx_dept 和 idx_name 在组合查询时只能用一个MySQL 会选选择性高的那个。改成复合索引覆盖两个条件一次定位。-- 删除单列索引建复合索引注意字段顺序等值在前范围在后 DROP INDEX idx_dept ON employee; CREATE INDEX idx_dept_name ON employee(dept_id, emp_name);逻辑说明复合索引遵循最左前缀dept_id 在前是因为按部门过滤通常是等值条件emp_name 的 LIKE 是范围条件放后面。查询 WHERE dept_id? AND emp_name LIKE ? 能完整命中这个索引。用 EXPLAIN 看 type 从 ALL 变成 ref 或 range 就说明生效了。再说存储过程。统计各部门平均薪资这种聚合写成存储过程让数据库算完再返回比拉全表到 Python 里算快得多也更能体现 mysql 存储过程的理解。DELIMITER // CREATE PROCEDURE dept_avg_salary() BEGIN SELECT d.dept_name, COUNT(e.emp_id) AS emp_count, IFNULL(AVG(e.salary), 0) AS avg_salary FROM department d LEFT JOIN employee e ON d.dept_id e.dept_id AND e.status 1 GROUP BY d.dept_id, d.dept_name ORDER BY avg_salary DESC; END // DELIMITER //逻辑说明用 LEFT JOIN 保证没有员工的部门也出现在结果里IFNULL 把空部门平均薪资显示为 0。status1 过滤掉离职员工条件写在 ON 里而不是 WHERE否则 LEFT JOIN 会退化成 INNER JOIN。DELIMITER 改分隔符是因为过程体里有分号不改客户端会提前截断。Python 侧调用存储过程def get_dept_avg(): with get_conn() as conn: with conn.cursor() as cur: cur.callproc(dept_avg_salary) return cur.fetchall()逻辑说明callproc 传过程名即可有入参就按顺序跟在后面。返回结果用 fetchall 取和普通查询一致。验证性能有没有提升别凭感觉。开 MySQL 慢查询日志把 long_query_time 设成 0.1 秒跑一遍业务操作看哪些 SQL 进了慢日志再针对性加索引。这个习惯我从课程设计一直带到工作比任何玄学调优都靠谱。索引不是越多越好每个索引都会拖慢写入员工表这种读多写少的场景加两三个复合索引足够别给每个列都建。最后说个我自己的教训当年做课程设计为了显得功能多给员工表加了十几个字段还全建了索引结果插入一千条测试数据慢得离谱答辩演示时卡住当场尴尬。后来才明白索引要跟着查询走不是跟着字段走。先想清楚系统有哪几个高频查询再倒推该建什么索引这个顺序反了就是白干。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?