# 数据库约束设计 **本文档引用的文件** - [models.py](file://backend/app/models/models.py) - [finance.py](file://backend/app/models/finance.py) - [001_initial.py](file://backend/alembic/versions/001_initial.py) - [002_template.py](file://backend/alembic/versions/002_template.py) - [database.py](file://backend/app/core/database.py) - [config.py](file://backend/app/core/config.py) - [database.md](file://docs/database.md) - [main.py](file://backend/app/main.py) ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构概览](#架构概览) 5. [详细组件分析](#详细组件分析) 6. [依赖关系分析](#依赖关系分析) 7. [性能考虑](#性能考虑) 8. [故障排除指南](#故障排除指南) 9. [结论](#结论) ## 简介 本文件详细阐述了医院绩效管理系统的数据库约束设计,涵盖唯一性约束、检查约束、索引设计和外键约束的实现原理与业务意义。通过对系统中各个数据表的约束策略进行深入分析,帮助开发者和运维人员理解数据完整性保障机制,并提供针对约束冲突的处理策略和优化建议。 ## 项目结构 该系统采用基于 SQLAlchemy 的 ORM 设计,数据库约束主要通过以下方式实现: - 数据模型层定义:在模型类中直接声明约束和索引 - 迁移脚本层:通过 Alembic 版本化管理数据库结构变更 - 配置层:数据库连接和会话管理 ```mermaid graph TB subgraph "数据模型层" Models[ORM 模型定义] Constraints[约束声明] Indexes[索引定义] end subgraph "迁移管理层" Alembic[Alembic 迁移] Initial[初始版本] Template[模板版本] end subgraph "配置层" Config[数据库配置] Engine[异步引擎] Session[会话管理] end Models --> Alembic Constraints --> Alembic Indexes --> Alembic Alembic --> Initial Alembic --> Template Config --> Engine Engine --> Session ``` **图表来源** - [models.py](file://backend/app/models/models.py#L62-L85) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L22-L84) - [database.py](file://backend/app/core/database.py#L10-L20) **章节来源** - [models.py](file://backend/app/models/models.py#L1-L50) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L1-L20) - [database.py](file://backend/app/core/database.py#L1-L39) ## 核心组件 系统的核心数据模型包括科室、员工、考核指标、考核记录、工资记录等关键实体。每个实体都配备了相应的约束策略以确保数据完整性。 ### 主要数据实体 | 实体名称 | 主键字段 | 唯一约束 | 外键约束 | 检查约束 | |---------|---------|---------|---------|---------| | departments | id | code | 无 | 无 | | staff | id | employee_id | department_id | 无 | | indicators | id | code | 无 | weight > 0 | | assessments | id | 无 | staff_id, assessor_id, reviewer_id | 无 | | assessment_details | id | 无 | assessment_id, indicator_id | 无 | | salary_records | id | 无 | staff_id | 无 | | users | id | username | staff_id | 无 | | performance_plans | id | plan_code | department_id, staff_id, parent_plan_id, submitter_id, approver_id | 无 | | plan_kpi_relations | id | (plan_id, indicator_id) 唯一 | plan_id, indicator_id | 无 | | indicator_templates | id | template_code | 无 | 无 | | template_indicators | id | (template_id, indicator_id) 唯一 | template_id, indicator_id | 无 | | department_finances | id | 无 | department_id | amount >= 0 | **章节来源** - [models.py](file://backend/app/models/models.py#L62-L437) - [finance.py](file://backend/app/models/finance.py#L45-L74) ## 架构概览 系统采用三层约束设计架构: ```mermaid graph TB subgraph "约束层次" Layer1[业务层约束
模型定义] Layer2[持久层约束
数据库层面] Layer3[应用层约束
应用程序逻辑] end subgraph "约束类型" Unique[唯一性约束] Check[检查约束] FK[外键约束] Index[索引约束] end subgraph "应用场景" Business[业务规则] DataIntegrity[数据完整性] QueryOptimization[查询优化] Referential[参照完整性] end Layer1 --> Unique Layer1 --> Check Layer1 --> FK Layer1 --> Index Unique --> Business Check --> DataIntegrity FK --> Referential Index --> QueryOptimization ``` **图表来源** - [models.py](file://backend/app/models/models.py#L68-L68) - [models.py](file://backend/app/models/models.py#L145-L145) - [finance.py](file://backend/app/models/finance.py#L73-L73) ## 详细组件分析 ### 唯一性约束设计 唯一性约束是确保数据唯一性的基础机制,系统在多个关键字段上实施了唯一性约束。 #### 唯一性约束实现 | 表名 | 唯一字段 | 约束目的 | 业务意义 | |------|---------|---------|---------| | departments | code | 科室编码唯一性 | 防止重复科室标识,便于精确识别 | | staff | employee_id | 员工工号唯一性 | 确保员工身份唯一标识,避免重复录入 | | users | username | 用户名唯一性 | 保证系统用户身份唯一,防止登录冲突 | | indicators | code | 指标编码唯一性 | 确保考核指标的唯一标识,便于管理和查询 | | performance_plans | plan_code | 计划编码唯一性 | 防止重复计划标识,维护计划体系完整性 | | indicator_templates | template_code | 模板编码唯一性 | 确保模板的唯一标识,便于模板管理和复用 | | plan_kpi_relations | (plan_id, indicator_id) | 关联关系唯一性 | 防止重复关联,确保计划-指标关系的准确性 | | template_indicators | (template_id, indicator_id) | 关联关系唯一性 | 防止重复关联,确保模板-指标关系的准确性 | #### 唯一性约束触发条件 ```mermaid flowchart TD Start([数据插入/更新]) --> CheckUnique["检查唯一性约束"] CheckUnique --> UniqueExists{"是否存在重复值?"} UniqueExists --> |是| RaiseError["抛出唯一性约束异常"] UniqueExists --> |否| Continue["继续执行操作"] RaiseError --> HandleConflict["处理冲突"] HandleConflict --> Rollback["回滚事务"] Continue --> Commit["提交事务"] Rollback --> End([结束]) Commit --> End ``` **图表来源** - [001_initial.py](file://backend/alembic/versions/001_initial.py#L37-L37) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L61-L61) - [002_template.py](file://backend/alembic/versions/002_template.py#L36-L36) **章节来源** - [001_initial.py](file://backend/alembic/versions/001_initial.py#L27-L28) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L46-L47) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L160-L160) - [002_template.py](file://backend/alembic/versions/002_template.py#L27-L28) ### 检查约束设计 检查约束用于强制执行复杂的业务规则,确保数据符合预定义的条件。 #### 检查约束实现 | 表名 | 约束条件 | 约束名称 | 业务含义 | |------|---------|---------|---------| | indicators | weight > 0 | ck_indicator_weight | 确保指标权重必须为正数,保证计算逻辑正确性 | | department_finances | amount >= 0 | ck_finance_amount | 确保财务金额不能为负数,维护财务数据准确性 | #### 检查约束验证流程 ```mermaid sequenceDiagram participant Client as 客户端 participant DB as 数据库 participant Trigger as 检查约束触发器 Client->>DB : INSERT/UPDATE 操作 DB->>Trigger : 验证检查约束 Trigger->>Trigger : 计算约束条件 Trigger->>Trigger : 判断条件是否满足 alt 条件满足 Trigger->>DB : 允许操作 DB-->>Client : 操作成功 else 条件不满足 Trigger->>DB : 拒绝操作 DB-->>Client : 抛出检查约束异常 end ``` **图表来源** - [models.py](file://backend/app/models/models.py#L145-L145) - [finance.py](file://backend/app/models/finance.py#L73-L73) **章节来源** - [models.py](file://backend/app/models/models.py#L143-L146) - [finance.py](file://backend/app/models/finance.py#L68-L74) ### 外键约束设计 外键约束确保参照完整性,防止出现孤立记录和无效引用。 #### 外键约束实现 | 表名 | 外键字段 | 引用表 | 约束类型 | 业务意义 | |------|---------|--------|---------|---------| | staff | department_id | departments | 必须引用有效科室 | 确保员工属于存在的科室 | | assessments | staff_id | staff | 必须引用有效员工 | 确保考核记录对应有效员工 | | assessments | assessor_id | staff | 可选引用有效员工 | 确保考核人存在且有效 | | assessments | reviewer_id | staff | 可选引用有效员工 | 确保审核人存在且有效 | | assessment_details | assessment_id | assessments | 必须引用有效考核记录 | 确保明细对应有效考核 | | assessment_details | indicator_id | indicators | 必须引用有效指标 | 确保明细对应有效指标 | | salary_records | staff_id | staff | 必须引用有效员工 | 确保工资记录对应有效员工 | | users | staff_id | staff | 可选引用有效员工 | 确保用户关联有效员工 | | performance_plans | department_id | departments | 可选引用有效科室 | 确保计划关联有效科室 | | performance_plans | staff_id | staff | 可选引用有效员工 | 确保计划关联有效员工 | | performance_plans | parent_plan_id | performance_plans | 自引用 | 确保计划层级关系正确 | | performance_plans | submitter_id | users | 可选引用有效用户 | 确保提交人存在且有效 | | performance_plans | approver_id | users | 可选引用有效用户 | 确保审批人存在且有效 | | plan_kpi_relations | plan_id | performance_plans | 必须引用有效计划 | 确保关联关系有效 | | plan_kpi_relations | indicator_id | indicators | 必须引用有效指标 | 确保关联关系有效 | | template_indicators | template_id | indicator_templates | 必须引用有效模板 | 确保关联关系有效 | | template_indicators | indicator_id | indicators | 必须引用有效指标 | 确保关联关系有效 | | department_finances | department_id | departments | 必须引用有效科室 | 确保财务记录对应有效科室 | #### 外键约束级联行为 ```mermaid classDiagram class Department { +id : int +name : str +code : str +children : Department[] } class Staff { +id : int +employee_id : str +department_id : int +department : Department } class Assessment { +id : int +staff_id : int +staff : Staff +assessor_id : int +reviewer_id : int } class PerformancePlan { +id : int +department_id : int +staff_id : int +parent_plan_id : int +submitter_id : int +approver_id : int +parent_plan : PerformancePlan } Department --> Staff : "1 : N" Staff --> Assessment : "1 : N" PerformancePlan --> PerformancePlan : "self-ref" ``` **图表来源** - [models.py](file://backend/app/models/models.py#L70-L70) - [models.py](file://backend/app/models/models.py#L154-L154) - [models.py](file://backend/app/models/models.py#L283-L283) **章节来源** - [models.py](file://backend/app/models/models.py#L95-L95) - [models.py](file://backend/app/models/models.py#L154-L154) - [models.py](file://backend/app/models/models.py#L281-L281) ### 索引设计策略 索引设计直接影响查询性能和数据检索效率,系统针对高频查询场景建立了多维索引。 #### 单列索引设计 | 表名 | 索引字段 | 索引名称 | 查询场景 | 性能收益 | |------|---------|---------|---------|---------| | departments | dept_type | idx_dept_type | 按科室类型查询 | 快速筛选特定类型科室 | | departments | parent_id | idx_dept_parent | 按上级科室查询 | 快速获取子科室层级 | | staff | department_id | idx_staff_dept | 按科室查询员工 | 快速定位科室员工 | | staff | status | idx_staff_status | 按状态查询员工 | 快速筛选特定状态员工 | | indicators | indicator_type | idx_indicator_type | 按指标类型查询 | 快速筛选指标类型 | | assessments | staff_id | idx_assessment_staff | 按员工查询考核记录 | 快速获取员工考核历史 | | assessments | period_year, period_month | idx_assessment_period | 按时间段查询考核记录 | 快速定位特定期间记录 | | assessments | status | idx_assessment_status | 按状态查询考核记录 | 快速筛选特定状态记录 | | assessment_details | assessment_id | idx_detail_assessment | 按考核记录查询明细 | 快速获取考核明细 | | assessment_details | indicator_id | idx_detail_indicator | 按指标查询明细 | 快速获取指标相关明细 | | salary_records | staff_id | idx_salary_staff | 按员工查询工资记录 | 快速获取员工工资历史 | | salary_records | period_year, period_month | idx_salary_period | 按时间段查询工资记录 | 快速定位特定期间工资 | | users | username | idx_user_username | 按用户名查询用户 | 快速定位用户账户 | | performance_plans | plan_level | idx_plan_level | 按计划层级查询 | 快速筛选特定层级计划 | | performance_plans | plan_year | idx_plan_year | 按年度查询计划 | 快速定位年度计划 | | performance_plans | department_id | idx_plan_department | 按科室查询计划 | 快速获取科室计划 | | performance_plans | status | idx_plan_status | 按状态查询计划 | 快速筛选特定状态计划 | | indicator_templates | template_type | idx_template_type | 按模板类型查询 | 快速筛选模板类型 | | indicator_templates | is_active | idx_template_active | 按启用状态查询 | 快速筛选有效模板 | | plan_kpi_relations | plan_id | idx_relation_plan | 按计划查询关联 | 快速获取计划关联指标 | | plan_kpi_relations | indicator_id | idx_relation_indicator | 按指标查询关联 | 快速获取指标关联计划 | | template_indicators | template_id | idx_ti_template | 按模板查询关联 | 快速获取模板关联指标 | | template_indicators | indicator_id | idx_ti_indicator | 按指标查询关联 | 快速获取指标关联模板 | | department_finances | department_id | idx_finance_dept | 按科室查询财务记录 | 快速获取科室财务数据 | | department_finances | period_year, period_month | idx_finance_period | 按时间段查询财务记录 | 快速定位特定期间财务 | | department_finances | finance_type | idx_finance_type | 按财务类型查询 | 快速筛选收支类型 | | department_finances | category | idx_finance_category | 按类别查询财务记录 | 快速筛选特定类别 | #### 复合索引设计思路 复合索引通过组合多个字段来优化特定查询模式: ```mermaid flowchart TD QueryPattern[查询模式分析] --> IdentifyFields["识别查询字段"] IdentifyFields --> Selectivity["评估选择性"] Selectivity --> OrderFields["确定字段顺序"] OrderFields --> TestPerformance["测试查询性能"] TestPerformance --> CreateIndex["创建复合索引"] CreateIndex --> MonitorUsage["监控索引使用"] MonitorUsage --> OptimizeIndex["优化索引设计"] subgraph "复合索引示例" PeriodIndex["idx_assessment_period
(year, month)"] RelationIndex["idx_ti_unique
(template_id, indicator_id)"] PlanIndex["idx_plan_department
(department_id, status)"] end ``` **图表来源** - [models.py](file://backend/app/models/models.py#L176-L176) - [models.py](file://backend/app/models/models.py#L436-L436) - [models.py](file://backend/app/models/models.py#L309-L309) **章节来源** - [models.py](file://backend/app/models/models.py#L82-L85) - [models.py](file://backend/app/models/models.py#L111-L114) - [models.py](file://backend/app/models/models.py#L174-L178) - [models.py](file://backend/app/models/models.py#L227-L230) - [models.py](file://backend/app/models/models.py#L258-L260) - [models.py](file://backend/app/models/models.py#L306-L311) - [models.py](file://backend/app/models/models.py#L433-L437) ### 约束冲突处理策略 当数据库约束被违反时,系统需要有一套完善的冲突处理机制。 #### 错误处理流程 ```mermaid sequenceDiagram participant App as 应用程序 participant DB as 数据库 participant Handler as 错误处理器 App->>DB : 执行数据库操作 DB->>DB : 验证所有约束 alt 约束验证失败 DB->>Handler : 抛出约束异常 Handler->>Handler : 分析异常类型 Handler->>Handler : 生成用户友好错误消息 Handler->>App : 返回错误响应 else 约束验证成功 DB->>App : 返回成功响应 end ``` #### 约束冲突类型及处理 | 冲突类型 | 触发条件 | 处理策略 | 用户反馈 | |---------|---------|---------|---------| | 唯一性冲突 | 插入重复唯一值 | 提示用户修改唯一字段值 | "该[字段]已存在,请使用其他值" | | 外键冲突 | 引用不存在的外键值 | 提示用户选择有效引用值 | "请选择有效的[关联实体]" | | 检查约束冲突 | 数据不符合检查条件 | 提示用户修正数据格式 | "[字段]必须满足[条件说明]" | | 级联删除冲突 | 尝试删除有子记录的父记录 | 提示用户先删除子记录或调整级联策略 | "请先删除子记录或调整关联关系" | **章节来源** - [main.py](file://backend/app/main.py#L58-L74) ## 依赖关系分析 系统约束设计涉及多个层面的依赖关系,形成了完整的数据完整性保障体系。 ```mermaid graph TB subgraph "模型层依赖" ModelBase[Base Model] ConstraintMixin[Constraint Mixin] IndexMixin[Index Mixin] end subgraph "约束实现" UniqueConstraint[Unique Constraint] CheckConstraint[Check Constraint] ForeignKeyConstraint[Foreign Key Constraint] IndexConstraint[Index Constraint] end subgraph "迁移层依赖" AlembicOps[Alembic Operations] MigrationScripts[Migration Scripts] end subgraph "配置层依赖" DatabaseConfig[Database Configuration] AsyncEngine[Async Engine] SessionManager[Session Manager] end ModelBase --> ConstraintMixin ModelBase --> IndexMixin ConstraintMixin --> UniqueConstraint ConstraintMixin --> CheckConstraint IndexMixin --> IndexConstraint AlembicOps --> MigrationScripts DatabaseConfig --> AsyncEngine AsyncEngine --> SessionManager MigrationScripts --> UniqueConstraint MigrationScripts --> CheckConstraint MigrationScripts --> ForeignKeyConstraint ``` **图表来源** - [models.py](file://backend/app/models/models.py#L23-L25) - [database.py](file://backend/app/core/database.py#L10-L20) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L21-L21) **章节来源** - [models.py](file://backend/app/models/models.py#L1-L13) - [database.py](file://backend/app/core/database.py#L1-L39) - [config.py](file://backend/app/core/config.py#L18-L21) ## 性能考虑 合理的约束设计不仅保证数据完整性,还能显著提升系统性能。 ### 索引性能优化 #### 查询性能分析 | 查询类型 | 索引使用情况 | 性能影响 | 优化建议 | |---------|---------|---------|---------| | 等值查询 | 使用精确匹配索引 | 高性能 | 确保查询条件包含索引字段 | | 范围查询 | 使用范围扫描索引 | 中等性能 | 优化查询条件,减少扫描范围 | | 复合查询 | 使用复合索引 | 高性能 | 确保查询条件覆盖索引前缀 | | 排序查询 | 使用排序索引 | 高性能 | 确保ORDER BY字段包含在索引中 | | 连接查询 | 使用连接索引 | 高性能 | 确保JOIN字段建立索引 | #### 索引维护策略 ```mermaid flowchart TD Monitor[监控索引使用] --> AnalyzeUsage["分析索引使用率"] AnalyzeUsage --> IdentifyUnused["识别未使用索引"] IdentifyUnused --> DropUnused["删除无用索引"] AnalyzeUsage --> IdentifyMissing["识别缺失索引"] IdentifyMissing --> CreateIndex["创建必要索引"] CreateIndex --> Monitor DropUnused --> Monitor Monitor --> RebuildIndex["重建碎片索引"] RebuildIndex --> Monitor ``` ### 约束对性能的影响 | 约束类型 | 正面影响 | 负面影响 | 性能平衡点 | |---------|---------|---------|---------| | 唯一性约束 | 数据一致性保证 | 插入/更新时额外检查 | 在高频写入场景下适度放宽 | | 检查约束 | 业务规则强制执行 | 数据验证开销 | 复杂检查约束需要谨慎使用 | | 外键约束 | 参照完整性保障 | 关联查询性能影响 | 对频繁关联查询考虑性能权衡 | | 索引约束 | 查询性能提升 | 写入性能下降 | 基于查询模式优化索引设计 | ## 故障排除指南 ### 常见约束问题诊断 #### 唯一性约束冲突 **问题症状**:插入或更新数据时报唯一性约束错误 **诊断步骤**: 1. 检查冲突的具体字段和值 2. 验证是否存在重复数据 3. 确认业务逻辑是否允许重复 4. 检查数据清理流程 **解决方案**: - 修改唯一字段值 - 清理重复数据 - 调整业务逻辑 - 重新设计唯一性策略 #### 外键约束冲突 **问题症状**:引用不存在的数据时报外键约束错误 **诊断步骤**: 1. 确认被引用表中是否存在对应记录 2. 检查关联字段的数据类型和格式 3. 验证数据插入顺序 4. 检查是否有级联删除策略 **解决方案**: - 先插入父记录再插入子记录 - 使用正确的关联值 - 调整级联删除策略 - 实施数据预校验 #### 检查约束冲突 **问题症状**:数据不符合检查条件时报错 **诊断步骤**: 1. 检查具体违反的约束条件 2. 验证数据格式和范围 3. 确认业务规则合理性 4. 检查数据转换逻辑 **解决方案**: - 修正数据格式 - 调整业务规则 - 实施数据验证 - 优化约束条件 ### 数据库迁移问题 #### 迁移失败处理 **常见问题**: - 字段已存在导致迁移失败 - 索引创建冲突 - 外键约束依赖问题 **处理策略**: 1. 检查当前数据库状态 2. 手动执行必要的DDL操作 3. 调整迁移脚本顺序 4. 实施回滚和重试机制 **章节来源** - [002_template.py](file://backend/alembic/versions/002_template.py#L67-L90) ## 结论 本系统的数据库约束设计体现了多层次、全方位的数据完整性保障机制。通过合理运用唯一性约束、检查约束、外键约束和索引约束,系统在保证数据准确性和一致性的前提下,实现了高效的查询性能和良好的扩展性。 ### 设计亮点 1. **层次化约束设计**:从模型层到数据库层的完整约束覆盖 2. **业务导向的约束策略**:每个约束都有明确的业务意义和触发条件 3. **性能优化的索引设计**:针对高频查询场景的复合索引策略 4. **完善的冲突处理机制**:从技术层面到用户体验的全面考虑 ### 持续改进建议 1. **监控和分析**:建立约束使用情况的监控机制 2. **定期优化**:根据查询模式变化调整索引策略 3. **文档完善**:持续更新约束设计文档和最佳实践 4. **性能测试**:定期进行约束对性能影响的评估测试 通过这套完整的约束设计体系,系统能够可靠地支撑医院绩效管理的各项业务需求,为医疗质量管理提供坚实的数据基础。