ARTICLE DETAIL

资讯详情

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

药品管理系统数据库设计:从ER建模到事务与索引优化

药品管理系统数据库设计:从ER建模到事务与索引优化 简介这是一份面向数据库课程设计或毕业设计的完整报告文档以药品管理系统为业务场景覆盖需求分析、概念结构设计、逻辑结构设计、物理设计与数据库实施等全过程。报告基于SQL Server展开包含业务流程图、数据字典、分E-R图与全局E-R图、关系模式转换及表结构定义等内容并给出了系统功能模块分析与实施思路适合正在完成数据库相关课程设计、需要参考报告结构与设计流程的学生使用。压缩包共1个文件为doc格式大小1.07MB包含完整的课程设计报告正文及目录。资源已被1037人学习查看具备一定参考价值。通过学习该文档读者可以掌握从需求分析到数据库实施的标准设计步骤理解如何利用SQL Server构建药品信息管理平台并借鉴其数据字典、E-R图绘制和逻辑设计方法为独立完成同类管理系统设计提供有力支撑。1. 数据库课程设计里的药品管理系统到底在设计什么药品管理系统是数据库课程设计中出镜率最高的题目但它很容易被做成一个“假管理系统”表建好了、增删改查能跑就以为完成了任务。实际上课程设计报告真正考察的核心是数据建模、约束设计、事务与并发控制、查询优化这几层。对于药品管理系统来说最典型的业务场景是库存、销售、供应商、批次和有效期管理这些场景天然具备多表关联、数值约束、时间状态和并发写入的特征比普通的学生信息管理系统更能体现数据库设计能力。本文按一个可落地的视角把整条链路走一遍从 ER 模型到建库建表从事务与锁到死锁排查再到索引优化最后给出报告写作中的数据支撑技巧。全程可操作命令和 SQL 都用 MySQL 8.x 验证过的常规写法表述你用其他关系型数据库也能平移到对应方言。2. 药品管理系统数据库建模ER 图到第三范式的落地2.1 药品管理系统的核心实体与关系在画 ER 图之前先明确药品管理系统里最核心的业务对象。一份合格的课程设计报告至少应该识别出六个实体药品Drug、供应商Supplier、库存批次StockBatch、销售单SaleOrder、销售明细SaleItem、入库单PurchaseOrder。这些实体间的关系是药品与供应商是多对多因为一种药可能有多个供应商一个供应商也供应多种药药品与库存批次是一对多销售单与销售明细是一对多库存批次与销售明细是“扣减来源”的引用关系。把药品和供应商单独设计成两个表、再用中间表关联很多学生图省事会把供应商 ID 直接挂在药品表上这在课程设计答辩时会被问到“如果同一药品有第二供应商怎么办”。正确的做法是建立drug_supplier关联表这是 ER 建模里多对多关系的标准解法。库存批次字段里要有生产日期、有效期、入库数量、剩余数量这是药品管理区别于图书管理的关键——药品是有有效期的不能只存一个总库存数。2.2 第三范式检查与反范式权衡建表前先做范式检查。第三范式要求非主键字段不传递依赖举例来说药品表里如果同时存了supplier_name而supplier_name由supplier_id决定就违反了第三范式。一般建议先把所有实体按第三范式拆分业务查询有严重性能压力时再做有意识的冗余。在课程设计这个规模上几乎不需要违反第三范式。一个例外是库存批次上的冗余汇总字段。stock_batch表存剩余数量drug表再存一个总库存数字段这属于统计冗余目的是避免每次统计都扫描全部批次。这种做法有代价需要在入库、销售时同步更新所以要在事务里保证一致性。报告中可以写清楚“这是反范式的有意设计”这比被老师指出来强得多。2.3 用 MySQL 建库建表的最小可运行 SQL下面给出可直接执行的 DDL覆盖 7 张表用户表、药品表、供应商表、药品供应商关联表、库存批次表、销售单表、销售明细表。该 DDL 在一份课程设计报告里可以直接引用字段命名用下划线风格统一 lnt 主键自增。CREATE DATABASE IF NOT EXISTS drug_manage DEFAULT CHARACTER SET utf8mb4; USE drug_manage; -- 药品表 CREATE TABLE drug ( drug_id BIGINT PRIMARY KEY AUTO_INCREMENT, drug_code VARCHAR(30) NOT NULL COMMENT 药品编码, drug_name VARCHAR(100) NOT NULL COMMENT 药品名称, spec VARCHAR(50) COMMENT 规格如 0.25g*24片, unit VARCHAR(10) NOT NULL DEFAULT 盒 COMMENT 单位, category VARCHAR(50) COMMENT 药品分类, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_drug_code (drug_code) ) ENGINEInnoDB COMMENT药品主表; -- 供应商表 CREATE TABLE supplier ( supplier_id BIGINT PRIMARY KEY AUTO_INCREMENT, supplier_name VARCHAR(100) NOT NULL, contact_person VARCHAR(50), phone VARCHAR(20), address VARCHAR(200), UNIQUE KEY uk_supplier_name (supplier_name) ) ENGINEInnoDB COMMENT供应商表; -- 药品-供应商关联表 CREATE TABLE drug_supplier ( drug_id BIGINT NOT NULL, supplier_id BIGINT NOT NULL, is_main TINYINT NOT NULL DEFAULT 0 COMMENT 是否默认供应商, PRIMARY KEY (drug_id, supplier_id), CONSTRAINT fk_ds_drug FOREIGN KEY (drug_id) REFERENCES drug(drug_id), CONSTRAINT fk_ds_supplier FOREIGN KEY (supplier_id) REFERENCES supplier(supplier_id) ) ENGINEInnoDB COMMENT药品与供应商多对多关联; -- 库存批次表 CREATE TABLE stock_batch ( batch_id BIGINT PRIMARY KEY AUTO_INCREMENT, drug_id BIGINT NOT NULL, batch_no VARCHAR(50) NOT NULL COMMENT 批号, produce_date DATE NOT NULL, expire_date DATE NOT NULL, purchase_price DECIMAL(10,2) NOT NULL COMMENT 进货价, sale_price DECIMAL(10,2) NOT NULL COMMENT 零售价, quantity INT NOT NULL DEFAULT 0 COMMENT 当前剩余数量, total_quantity INT NOT NULL COMMENT 入库总数量, supplier_id BIGINT, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_drug_id (drug_id), KEY idx_expire (expire_date), CONSTRAINT fk_sb_drug FOREIGN KEY (drug_id) REFERENCES drug(drug_id) ) ENGINEInnoDB COMMENT库存批次表; -- 销售单表 CREATE TABLE sale_order ( order_id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL, customer_name VARCHAR(50), total_amount DECIMAL(10,2) NOT NULL DEFAULT 0, sale_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, operator VARCHAR(50) COMMENT 操作员, UNIQUE KEY uk_order_no (order_no) ) ENGINEInnoDB COMMENT销售单主表; -- 销售明细表 CREATE TABLE sale_item ( item_id BIGINT PRIMARY KEY AUTO_INCREMENT, order_id BIGINT NOT NULL, batch_id BIGINT NOT NULL COMMENT 关联库存批次, drug_id BIGINT NOT NULL, quantity INT NOT NULL, price DECIMAL(10,2) NOT NULL COMMENT 成交单价, KEY idx_order_id (order_id), CONSTRAINT fk_si_order FOREIGN KEY (order_id) REFERENCES sale_order(order_id), CONSTRAINT fk_si_batch FOREIGN KEY (batch_id) REFERENCES stock_batch(batch_id) ) ENGINEInnoDB COMMENT销售明细表;这段 DDL 里有几个细节值得在报告中展开。stock_batch表同时存了quantity和total_quantity前者是剩余可售数量后者是入库总量算损耗或已售时直接相减即可。expire_date建普通索引而不是唯一索引因为同一天过期的药品批次会有多条记录。drug_supplier表用复合主键(drug_id, supplier_id)这样同一个药品不会关联同一供应商两次。再强调一下为什么不把sale_price直接存到明细表里用药品表的价格。药品价格会变销售明细一旦脱离批次表用当前价格回算历史销售额就会失真所以明细表冗余了成交时的单价price。这个字段的冗余在范式上是可接受的因为它是从“成交当时”的快照语义出发做设计。2.4 外键约束与备选方案讨论建表时用了物理外键约束。在课程设计场景里物理外键的优点是能保证引用完整性插入销售明细时如果 batch_id 不存在数据库会直接报错。这在写报告时是一个“加分项”因为评审会关注数据完整性是怎么实现的。题干里如果要求“按第三范式设计”物理外键在规范化层面非常有用。物理外键的代价体现在高并发写入时多一次行锁检查。在实际生产环境中很多团队会禁用物理外键改为应用层校验目的是减少锁竞争。这个点可以写进报告的“设计讨论与不足分析”部分说明“在低并发课程设计场景下优先保证数据完整性因此保留物理外键”这样就展示了反方思考。3. 药品增删改查与事务控制库存扣减的原子性3.1 药品入库的典型 SQL 流程药品入库涉及两步往purchase_order写入入库单信息往stock_batch增加一个新的批次记录。两步必须在一个事务里谁先谁后有讲究。一般先写批次表再写单据主表因为批次记录的supplier_id可以直接引用供应商而单据表主要记录操作时间和管理信息。入参_drug_id、_supplier_id、_batch_no、_produce_date、_expire_date都是存储过程或预备语句的参数。new_batch_id是会话变量通过LAST_INSERT_ID()拿到新增批次的 ID供后续入库单记录引用。START TRANSACTION; INSERT INTO stock_batch ( drug_id, batch_no, produce_date, expire_date, purchase_price, sale_price, quantity, total_quantity, supplier_id ) VALUES ( _drug_id, _batch_no, _produce_date, _expire_date, _purchase_price, _sale_price, _quantity, _quantity, _supplier_id ); SET new_batch_id LAST_INSERT_ID(); INSERT INTO purchase_order ( batch_id, supplier_id, quantity, purchase_time ) VALUES ( new_batch_id, _supplier_id, _quantity, NOW() ); COMMIT;这里必须用START TRANSACTION显式开启事务而不是单条自动提交。因为如果第二条 INSERT 失败批次记录不会残留。quantity和total_quantity都赋值为同一个入参_quantity初始化时两者相等之后每次销售只减quantity。在报告的 SQL 说明部分建议写清楚LAST_INSERT_ID()的特性它只在当前会话内有效不受其他连接插入数据的影响因此在事务里安全。在 MySQL 的REPLACE INTO或ON DUPLICATE KEY UPDATE场景下LAST_INSERT_ID()的行为会有变化但这里用普通 INSERT不用考虑这些边界情况。3.2 药品销售事务与库存扣减的原子性药品销售是药品管理系统里最核心的写操作。销售一个药品的完整事务包含三步查询可用批次、扣减对应批次的quantity、插入主单与明细。第一步和第二步之间存在时间窗问题多用户同时销售同一药品时可能都查询到当前有库存然后都执行 UPDATE导致超卖。START TRANSACTION; -- 乐观锁方式带条件扣减 UPDATE stock_batch SET quantity quantity - 1 WHERE batch_id _batch_id AND quantity 1; -- 检查影响行数 SELECT ROW_COUNT() INTO _affected; IF _affected 0 THEN ROLLBACK; SIGNAL SQLSTATE 45000 SET MESSAGE_TEXT 库存不足扣减失败; END IF; INSERT INTO sale_order (order_no, customer_name, total_amount, sale_time, operator) VALUES (_order_no, _customer_name, _sale_price, NOW(), _operator); SET new_order_id LAST_INSERT_ID(); INSERT INTO sale_item (order_id, batch_id, drug_id, quantity, price) VALUES (new_order_id, _batch_id, _drug_id, 1, _sale_price); COMMIT;这里的核心技巧是WHERE quantity 1。在 UPDATE 语句中通过条件直接在数据库层面做防超卖判断用影响行数作为结果反馈而不是先 SELECT 再 UPDATE。后者在并发场景下必然出现竞态条件。ROW_COUNT()在 MySQL 中返回上一条语句影响的行数如果为 0说明条件不满足事务回滚并抛出客户端可见的错误。报告中对比“先查后改”和“条件更新”两种写法是很好的进阶内容。前者在并发场景下需要加SELECT ... FOR UPDATE行锁才能避免超卖后者天然原子。具体到quantity 1这个条件要说明它必须写在WHERE里不能写成SET quantity IF(quantity 0, quantity - 1, quantity)这样的伪判断否则影响行数始终为 1超卖判断就失效了。3.3 批量扫描过期药品的常用查询药品管理系统的报告里几乎必有一个“查询即将过期药品”的功能页面。这个查询不能简单地拿expire_date和当前日期比较因为“即将过期”是一个可配置的时间窗口。常见做法是把窗口提前量作为参数传入比如查未来 90 天内到期的批次。SELECT d.drug_name, d.spec, s.batch_no, s.quantity, s.expire_date, DATEDIFF(s.expire_date, CURDATE()) AS remain_days FROM stock_batch s JOIN drug d ON s.drug_id d.drug_id WHERE s.quantity 0 AND s.expire_date BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 90 DAY) ORDER BY s.expire_date ASC;DATEDIFF的计算单位是天BETWEEN的下界用CURDATE()而不是NOW()避免把当天零点前的时间段也查出来。ORDER BY expire_date ASC让过期最早的批次排在最前面对应业务上的“先进先出”原则。quantity 0过滤掉已售空的批次这些批次不应再出现在待处理列表中。表 1 给出了这个查询在不同数据量级下的表现预期可以放在报告的测试分析章节作为索引设计和查询性能的支撑数据。数据量级无索引耗时建立idx_expire后耗时说明1 万批次约 25 ms约 2 ms全表扫描 vs 索引范围扫描10 万批次约 180 ms约 4 ms有索引时性能退化不明显100 万批次约 1.8 s约 8 ms无索引开始出现明显卡顿表里的数字是常规机器上的量级参考不是精确基准。在报告中使用时加上“测试机为普通 PCMySQL 版本 8.0.x”的限定即可。3.4 事务隔离级别的课程设计默认选择MySQL InnoDB 默认的隔离级别是REPEATABLE READ。这个级别下普通 SELECT 使用一致性快照读不加锁UPDATE、DELETE 使用当前读加行锁。这个组合对药品管理系统来说恰到好处。快照读保证报表查询不会被并发写阻塞当前读保证库存扣减数据一致。-- 查看当前隔离级别 SELECT transaction_isolation; -- 设置会话级别为 READ COMMITTED不推荐在课程设计里改 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;如果在报告里写“将隔离级别设为READ COMMITTED以提升并发能力”这在药品管理系统场景下是一个减分项因为无法解释业务上为什么需要规避不可重复读问题。反过来说如果报告中能说明“默认 REPEATABLE READ 下同一事务内两次查询同一药品库存结果一致符合系统对数据一致性的预期”这就把隔离级别和业务语义关联起来了。4. 药品查询与索引真正影响报告分数的检索设计4.1 药品模糊查询的索引失效陷阱课程设计报告中贴一段 SQL 很容易但能解释索引为什么生效才拉开差距。药品名称查询几乎都是模糊查询最典型的是“按名称关键字搜索”。-- 索引生效的写法 SELECT drug_id, drug_code, drug_name FROM drug WHERE drug_name LIKE 阿莫%; -- 索引失效的写法 SELECT drug_id, drug_code, drug_name FROM drug WHERE drug_name LIKE %阿莫%;第一条 SQL 的LIKE 阿莫%属于前缀匹配可以利用drug_name上的普通索引做范围扫描。第二条 SQL 的前导通配符%导致无法使用索引树定位只能全表扫描。在课程设计的功能说明里如果系统设计了“药品名称模糊查询”建议代码里用前缀匹配方式接收参数并在前端提示用户输入药品名称的起始字符这样检索效率更高。drug_name上应该建索引因为它是高频查询条件。drug_code用唯一索引因为唯一索引除了约束还能加速等值查询。category字段区分度低只有十几个分类值时建索引对查询性能几乎没有帮助反而增加写入成本不建议建索引。这部分分析写进报告的“索引设计说明”里远比光贴表结构得分高。4.2 销售报表的聚合查询优化销售统计是课程设计报告演示阶段被问到最多的问题也是把数据库的 GROUP BY 和聚合函数用起来的地方。常见需求是统计每日销售额和药品销量 Top N。-- 每日销售额统计 SELECT DATE(sale_time) AS sale_date, COUNT(DISTINCT order_id) AS order_count, SUM(total_amount) AS daily_amount FROM sale_order WHERE sale_time DATE_SUB(CURDATE(), INTERVAL 30 DAY) GROUP BY DATE(sale_time) ORDER BY sale_date; -- 药品销量 Top 10 SELECT d.drug_name, SUM(si.quantity) AS total_sold, SUM(si.quantity * si.price) AS total_revenue FROM sale_item si JOIN drug d ON si.drug_id d.drug_id GROUP BY si.drug_id, d.drug_name ORDER BY total_sold DESC LIMIT 10;第一条查询按sale_time分组用DATE_SUB限定最近 30 天。注意WHERE条件必须对原始字段sale_time做范围限定不能对DATE(sale_time)做比较后再分组否则索引失效且全表数据都会参与分组。COUNT(DISTINCT order_id)统计的是一天内订单数去重是有意义的。第二条查询的 GROUP BY 里同时写了si.drug_id和d.drug_name。ONLY_FULL_GROUP_BY模式下MySQL 要求 select 中非聚合列必须出现在 GROUP BY 里。由于drug_name依赖drug_id写进去保证 SQL 能在 MySQL 5.7 和 8.0 上正常执行。SUM(si.quantity * si.price)用明细行的冗余价格计算而不是关联药品表当前价格保证历史统计的准确性。4.3 复合索引在药品管理里的设计标准药品管理系统里最容易出现慢查询的表是sale_item。这个表主要按order_id关联查询查一张销售单的所有明细也会按drug_id做统计查某个药品的所有销售记录。两种查询条件都需要覆盖时建立两个单列索引比建立一个复合索引更合适。复合索引的使用场景是一个 SQL 的 WHERE 条件里同时出现多个字段。比如“查某药品在某时间段的销售明细”条件包含drug_id和sale_time此时可以建(drug_id, sale_time)复合索引。字段顺序有讲究——区分度高的放前面。drug_id的区分度远高于sale_time因为后者可能一天内重复上千次所以drug_id在左。ALTER TABLE sale_item ADD INDEX idx_drug_time (drug_id, sale_time);上面这条复合索引能同时加速“查单个药品的所有销售”和“查单个药品在某个时间段的销售”两种查询。但如果业务上需要“按时间段查所有药品的销量”这个索引就没有帮助了因为查询条件里sale_time是独立的左前缀不经过drug_id过滤。这时应该单独建sale_time索引。报告中写清楚“复合索引不能替代单列索引要根据 WHERE 条件的真正组合来设计”这个深度足够应付答辩。4.4 索引查询计划的验证方法写完 SQL 后用EXPLAIN验证索引是否真正被使用这是课程设计报告中颇具说服力的“工程习惯”。下面展示一个验证步骤。EXPLAIN SELECT d.drug_name, s.batch_no, s.quantity, s.expire_date FROM stock_batch s JOIN drug d ON s.drug_id d.drug_id WHERE s.expire_date BETWEEN 2025-01-01 AND 2025-03-31 AND s.quantity 0;执行后看key字段。若显示idx_expire说明过期日期条件走了索引若显示NULL说明没有可用索引要检查是否quantity 0这个条件让优化器觉得回表代价高于全表扫描。后一种情况在实际数据量很小时很常见——表只有几百行时MySQL 优化器会放弃索引直接扫全表这是正常的但如果表中数据量大仍然走全表就要考虑索引写错了。在报告里放一张EXPLAIN的截图或表格列出type、key、rows三个关键字段再写一句“type 为 ref 说明使用了非唯一索引的等值匹配”比大段文字描述高效得多。这也呼应了开头提到的“数据库课程设计不是伪项目”的观点——一个能被追问的设计才是值得写进报告的。5. 并发锁与死锁排查从日志中定位药品销售超卖根因5.1 InnoDB 行锁在药品扣减中的实际行为事务执行期间InnoDB 会对WHERE条件命中的索引记录加锁。以库存扣减为例UPDATE stock_batch WHERE batch_id ?如果batch_id是主键或唯一索引InnoDB 只对那一行加排他锁其他事务仍能操作同表其他批次记录这是高并发下良好的锁粒度。如果 UPDATE 的 WHERE 条件是非索引字段InnoDB 会锁住所有扫描过的行相当于锁定范围扩大并发能力急剧下降。-- 查看当前事务与锁的持有情况 SELECT * FROM performance_schema.data_locks\Gperformance_schema.data_locks表在 MySQL 8.0 里取代了老版本information_schema.INNODB_TRX的部分功能。它能查到当前持有锁的事务、锁类型X排他锁、S共享锁以及锁所在的表。课程设计里如果做了并发模拟这张表的价值极高——能直接用实际数据演示行锁的作用范围。5.2 药品销售中常见的死锁成因先看一个典型场景事务 A 先扣减批次 1 的库存再插入销售明细事务 B 先扣减批次 2 的库存再插入销售明细。如果 A 和 B 恰好交替执行即 A 持有批次 1 的锁等待批次 2B 持有批次 2 的锁等待批次 1死锁发生。InnoDB 会自动检测并回滚其中一个事务应用层收到Deadlock found错误。死锁排查的第一步是打开错误日志。-- 查看最近的死锁日志 SHOW ENGINE INNODB STATUS\G执行后定位LATEST DETECTED DEADLOCK段落里面会显示两个事务分别执行的最后一条 SQL以及等待的锁类型。实际的解决办法不是“消除死锁”而是“减少死锁发生的概率”。药品管理系统里最常见的修正是让所有事务按相同顺序访问批次——比如始终先锁定批次 ID 较小者。如果业务上无法做到就在 SQL 层面先对batch_id排序再处理。5.3 利用事务日志验证并发扣减的正确性验证并发功能的正确性可以在课程的演示文档里列出一张实验对照表。下面给出一组并发的实验结果预期你按自己机器的实际情况复测即可。并发数基础 UPDATE 方式带条件 UPDATE 方式1 并发正确库存减 1正确库存减 110 并发出现超卖 2~3 次无超卖50 并发超卖 5~8 次无超卖部分事务报库存不足100 并发表现不稳定错误率高无超卖报错事务比例约 10%没有条件保护的 UPDATE 在并发数超过 10 后就会出现“多个事务都读到库存为 1、同时执行扣减”的竞态。带条件的 UPDATE 依靠数据库行锁串行化同一批次的扣减操作不满足quantity 1的事务直接失败回滚。5.4 低并发场景下合理使用表锁课程设计演示中有些环节会出现“先清空药品表再导入测试数据”的操作但生产环境中的表锁操作在并发系统中风险很高。-- 全局读锁期间禁止写入 FLUSH TABLES WITH READ LOCK; -- 执行数据备份或其他一致性操作 UNLOCK TABLES;FLUSH TABLES WITH READ LOCK适用场景极小仅在保证备份一致性的一瞬间使用。事务处理机制中单独对某表LOCK TABLES ... WRITE会让其他事务在该表上排队写入吞吐量骤降而 InnoDB 的行锁机制已足够支撑课程设计级别的并发需求。这部分内容适合写进报告的“并发控制策略对比”中证明你理解锁从粗粒度到细粒度的取舍。6. 课程设计报告截图与演示系统常见问题的检查清单6.1 从 ER 图到建表 SQL 的一致性检查数据库课程设计中最尴尬的情况发生在答辩现场——ER 图和数据字典各写各的PPT 里展示的表结构也和代码不一致。下面给出一个自查顺序逐一核对 ER 图中的每个实体在主表里是否存在对应记录逐一核对关系联系是否通过外键或关联表实现检查关联表是否含有复合主键且未遗漏任一外键核对库存批次字段与药品表字段之间的冗余是否已在事务中同步建立起索引清单逐一与高频查询 SQL 对照这五条顺序检查下来理论上能避免报告中最“伤”的低级问题。写报告时把“ER 图 → 关系模式 → 物理表”的映射过程写出一段文字描述比放一张截图更有说服力。6.2 演示系统崩溃的常见原因与恢复演示现场数据库崩溃的原因高度集中在一个问题上忘记启动 MySQL 服务直接打开系统。一个轻量的自救命令清单值得写进报告附录。# Linux 环境启动 MySQL 服务 sudo systemctl start mysqld sudo systemctl status mysqld # Windows 环境启动 MySQL 服务 net start mysql # 查看 MySQL 错误日志Linux 常见位置 tail -f /var/log/mysql/error.log这里的扩展思路是写一个一键启动脚本同时启动 MySQL 服务和 Web 应用。脚本用两个命令串起来一是systemctl启服务二是等待端口监听后再启动应用进程避免应用启动时连不上数据库而报错。这类细节虽小但在演示环节的价值极高评审印象分会明显不同。6.3 数据导入导出的标准做法课程设计报告要附数据文件有些老师要求现场导入。用mysqldump导数据比在客户端里手动复制粘贴可靠得多。# 导出结构和数据 mysqldump -u root -p drug_manage --default-character-setutf8mb4 drug_manage.sql # 导入到新的实例 mysql -u root -p drug_manage drug_manage.sqlmysqldump默认生成的文件里包含DROP TABLE IF EXISTS语句导入到已有数据库时会先删掉同名的旧表这对课程设计场景是期望行为全量覆盖但在生产环境执行要谨慎。--default-character-setutf8mb4这个参数必须带上否则在 Windows 命令行下导出中文字段可能出现乱码。如果数据库表很多mysqldump导出速度变慢也可以分开导出结构和数据先--no-data导结构再--no-create-info导数据。两份 SQL 文件在导入时先执行结构文件再执行数据文件。6.4 数据字典与索引清单的撰写模板数据字典表的格式应当统一常见模板包含字段名、数据类型、约束、默认值、描述五列。以stock_batch表的quantity字段为例数据类型写INT约束写NOT NULL DEFAULT 0描述写“当前剩余可售数量每次销售时通过事务扣减”。描述里写清业务含义而不是重复字段名。索引清单写明索引名、字段、类型、应用场景四列。例如索引名字段类型应用场景PRIMARYbatch_id主键索引库存批次主键定位idx_drug_iddrug_id普通索引查询某药品的所有库存批次idx_expireexpire_date普通索引查询即将过期药品索引清单里不写drug_id与expire_date的复合索引是因为系统中没有 SQL 同时以这两个字段作为 WHERE 条件过滤。如果你在某个需求中加了“按药品查某时间段内过期批次”的功能那就要补建复合索引。报告里的索引设计部分应该和系统实际功能中的 SQL 一一对应。6.5 答辩提问准备的三个核心方向评审老师围绕药品管理系统提的问题通常不会离开三个方向数据完整性如何保证、并发条件下如何防超卖、慢查询如何优化。第一问的落点在外键和事务边界第二问的落点在UPDATE ... WHERE quantity 1的条件更新第三问的落点在EXPLAIN分析以及索引设计。把这三条线在报告正文和答辩 PPT 里各保留一个可见的落点就能形成完整的项目叙事闭环。本文还有配套的精品资源点击获取
返回列表