# 索引与约束设计 **本文引用的文件** - [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) - [init_db.py](file://backend/init_db.py) ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖关系分析](#依赖关系分析) 7. [性能考量](#性能考量) 8. [故障排查指南](#故障排查指南) 9. [结论](#结论) 10. [附录](#附录) ## 简介 本文件聚焦于该医院绩效系统的数据库索引与约束设计,系统采用 SQLAlchemy ORM + Alembic 进行模型定义与迁移,结合 PostgreSQL 异步驱动,围绕“科室—员工—考核—指标—工资—计划—模板—菜单—财务”等核心业务实体,系统化梳理主键、外键、唯一索引、复合索引、检查约束的设计原则与性能影响,并给出查询优化建议与并发控制考虑。 ## 项目结构 - 数据模型集中于 models 目录,通过 Base 声明式基类统一建模。 - Alembic 版本化迁移文件定义了初始表结构与索引。 - 配置模块提供数据库连接参数与池化配置。 - 文档目录包含数据库 ER 图与表结构说明。 ```mermaid graph TB subgraph "模型层" M1["models.py
核心业务模型"] M2["finance.py
财务核算模型"] end subgraph "迁移层" V1["001_initial.py
初始版本"] V2["002_template.py
模板扩展"] end subgraph "运行时" C["config.py
配置"] D["database.py
引擎/会话"] I["init_db.py
初始化脚本"] end subgraph "文档" DOC["database.md
ER图与表说明"] end M1 --> V1 M2 --> V2 V1 --> D V2 --> D C --> D I --> D DOC -.参考.-> M1 ``` 图表来源 - [models.py](file://backend/app/models/models.py#L62-L438) - [finance.py](file://backend/app/models/finance.py#L45-L79) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L21-L183) - [002_template.py](file://backend/alembic/versions/002_template.py#L21-L96) - [database.py](file://backend/app/core/database.py#L9-L39) - [config.py](file://backend/app/core/config.py#L18-L22) - [database.md](file://docs/database.md#L1-L286) 章节来源 - [models.py](file://backend/app/models/models.py#L1-L438) - [finance.py](file://backend/app/models/finance.py#L1-L79) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L1-L183) - [002_template.py](file://backend/alembic/versions/002_template.py#L1-L96) - [database.py](file://backend/app/core/database.py#L1-L39) - [config.py](file://backend/app/core/config.py#L1-L47) - [database.md](file://docs/database.md#L1-L286) ## 核心组件 - 主键与自增:多数表使用整型自增主键,确保全局唯一性与插入性能。 - 外键约束:通过 ForeignKey 映射,形成稳定的父子关系与引用完整性。 - 唯一约束:对业务唯一字段(如科室编码、员工工号、指标编码、用户名、模板编码)施加唯一约束,避免重复。 - 索引策略:针对高频过滤/连接/排序字段建立单列索引;对多条件组合查询建立复合索引;对枚举字段建立索引提升筛选效率。 - 检查约束:对数值范围进行约束(如权重>0、金额>=0),保证数据质量与业务规则一致性。 章节来源 - [models.py](file://backend/app/models/models.py#L62-L438) - [finance.py](file://backend/app/models/finance.py#L45-L79) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L21-L183) - [002_template.py](file://backend/alembic/versions/002_template.py#L21-L96) ## 架构总览 系统采用分层架构,ORM 层负责模型映射与约束声明,迁移层负责表结构演进,运行时提供异步连接与会话管理。 ```mermaid graph LR A["前端(Vue)"] --> B["后端(FastAPI)"] B --> C["服务层"] C --> D["ORM(SQLAlchemy)"] D --> E["数据库(PostgreSQL)"] D --> F["索引/约束"] ``` 图表来源 - [database.py](file://backend/app/core/database.py#L9-L39) - [models.py](file://backend/app/models/models.py#L1-L438) ## 详细组件分析 ### 科室表(departments) - 主键:id(自增) - 唯一索引:code(科室编码) - 单列索引:dept_type(按类型过滤)、parent_id(树形结构查询) - 外键:parent_id 自引用(树形组织) - 设计要点:dept_type 用于快速筛选不同类型的科室;parent_id 支持层级查询;code 唯一保证业务唯一性。 ```mermaid erEntity "departments" { id : integer PK code : varchar(20) UK dept_type : enum parent_id : integer FK level : integer sort_order : integer is_active : boolean created_at : datetime updated_at : datetime } ``` 图表来源 - [models.py](file://backend/app/models/models.py#L62-L86) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L22-L41) 章节来源 - [models.py](file://backend/app/models/models.py#L62-L86) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L22-L41) ### 员工表(staff) - 主键:id(自增) - 唯一索引:employee_id(工号) - 单列索引:department_id(按科室统计/查询)、status(按状态筛选) - 外键:department_id → departments.id - 设计要点:department_id 与 status 的索引支持按科室聚合与状态筛选;unique 约束保证工号唯一。 ```mermaid erEntity "staff" { id : integer PK employee_id : varchar(20) UK department_id : integer FK status : enum created_at : datetime updated_at : datetime } ``` 图表来源 - [models.py](file://backend/app/models/models.py#L88-L114) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L42-L64) 章节来源 - [models.py](file://backend/app/models/models.py#L88-L114) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L42-L64) ### 考核指标表(indicators) - 主键:id(自增) - 唯一索引:code(指标编码) - 单列索引:indicator_type(按类型筛选) - 检查约束:weight > 0(权重必须为正数) - 设计要点:唯一编码保证业务唯一;按类型索引支持快速筛选;检查约束保证权重合法性。 ```mermaid erEntity "indicators" { id : integer PK code : varchar(20) UK indicator_type : enum weight : numeric(5,2) created_at : datetime updated_at : datetime } ``` 图表来源 - [models.py](file://backend/app/models/models.py#L117-L146) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L66-L85) 章节来源 - [models.py](file://backend/app/models/models.py#L117-L146) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L66-L85) ### 考核记录表(assessments) - 主键:id(自增) - 单列索引:staff_id(按员工查询)、status(按状态筛选) - 复合索引:period_year + period_month(按年月组合查询) - 外键:staff_id → staff.id;assessor_id/reviewer_id → staff.id(自关联) - 设计要点:复合索引支持按年月快速定位;staff_id 索引支撑员工维度统计;status 索引便于流程状态筛选。 ```mermaid erEntity "assessments" { id : integer PK staff_id : integer FK period_year : integer period_month : integer status : enum assessor_id : integer FK reviewer_id : integer FK created_at : datetime updated_at : datetime } ``` 图表来源 - [models.py](file://backend/app/models/models.py#L149-L178) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L87-L112) 章节来源 - [models.py](file://backend/app/models/models.py#L149-L178) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L87-L112) ### 考核明细表(assessment_details) - 主键:id(自增) - 单列索引:assessment_id(按考核记录查询明细)、indicator_id(按指标查询明细) - 外键:assessment_id → assessments.id;indicator_id → indicators.id - 设计要点:双外键字段分别建立单列索引,满足“按记录查明细”和“按指标查明细”的常见查询模式。 ```mermaid erEntity "assessment_details" { id : integer PK assessment_id : integer FK indicator_id : integer FK created_at : datetime updated_at : datetime } ``` 图表来源 - [models.py](file://backend/app/models/models.py#L181-L202) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L114-L131) 章节来源 - [models.py](file://backend/app/models/models.py#L181-L202) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L114-L131) ### 工资核算表(salary_records) - 主键:id(自增) - 单列索引:staff_id(按员工查询)、status(按状态筛选) - 复合索引:period_year + period_month(按年月组合查询) - 外键:staff_id → staff.id - 设计要点:复合索引支持按年月快速检索;staff_id 索引支撑员工维度统计。 ```mermaid erEntity "salary_records" { id : integer PK staff_id : integer FK period_year : integer period_month : integer status : varchar(20) created_at : datetime updated_at : datetime } ``` 图表来源 - [models.py](file://backend/app/models/models.py#L205-L230) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L133-L154) 章节来源 - [models.py](file://backend/app/models/models.py#L205-L230) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L133-L154) ### 用户表(users) - 主键:id(自增) - 唯一索引:username(用户名) - 外键:staff_id → staff.id(可选关联) - 设计要点:username 唯一索引支持登录认证;staff_id 外键实现用户与员工的弱关联。 ```mermaid erEntity "users" { id : integer PK username : varchar(50) UK staff_id : integer FK created_at : datetime updated_at : datetime } ``` 图表来源 - [models.py](file://backend/app/models/models.py#L244-L260) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L156-L172) 章节来源 - [models.py](file://backend/app/models/models.py#L244-L260) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L156-L172) ### 绩效计划表(performance_plans) - 主键:id(自增) - 唯一索引:plan_code(计划编码) - 单列索引:plan_level、plan_year、department_id、status - 外键:department_id → departments.id;staff_id → staff.id;submitter_id/approver_id → users.id;parent_plan_id → performance_plans.id(自关联) - 设计要点:多维索引支持按层级、年份、状态、科室等条件筛选;唯一编码保证业务唯一性。 ```mermaid erEntity "performance_plans" { id : integer PK plan_code : varchar(50) UK plan_level : enum plan_year : integer department_id : integer FK status : enum parent_plan_id : integer FK created_at : datetime updated_at : datetime } ``` 图表来源 - [models.py](file://backend/app/models/models.py#L270-L311) 章节来源 - [models.py](file://backend/app/models/models.py#L270-L311) ### 计划-指标关联表(plan_kpi_relations) - 主键:id(自增) - 单列索引:plan_id、indicator_id - 复合唯一索引:plan_id + indicator_id(唯一关联) - 外键:plan_id → performance_plans.id;indicator_id → indicators.id - 设计要点:复合唯一索引防止重复关联;单列索引支撑按计划或指标查询。 ```mermaid erEntity "plan_kpi_relations" { id : integer PK plan_id : integer FK indicator_id : integer FK plan_id+indicator_id : unique created_at : datetime updated_at : datetime } ``` 图表来源 - [models.py](file://backend/app/models/models.py#L314-L338) - [002_template.py](file://backend/alembic/versions/002_template.py#L41-L63) 章节来源 - [models.py](file://backend/app/models/models.py#L314-L338) - [002_template.py](file://backend/alembic/versions/002_template.py#L41-L63) ### 模板-指标关联表(template_indicators) - 主键:id(自增) - 单列索引:template_id、indicator_id - 复合唯一索引:template_id + indicator_id(唯一关联) - 外键:template_id → indicator_templates.id;indicator_id → indicators.id - 设计要点:与计划-指标关联一致的索引策略,确保模板维度的高效查询。 ```mermaid erEntity "template_indicators" { id : integer PK template_id : integer FK indicator_id : integer FK template_id+indicator_id : unique created_at : datetime updated_at : datetime } ``` 图表来源 - [models.py](file://backend/app/models/models.py#L411-L437) - [002_template.py](file://backend/alembic/versions/002_template.py#L41-L63) 章节来源 - [models.py](file://backend/app/models/models.py#L411-L437) - [002_template.py](file://backend/alembic/versions/002_template.py#L41-L63) ### 财务核算表(department_finances) - 主键:id(自增) - 单列索引:department_id、finance_type、category - 复合索引:period_year + period_month(按年月组合查询) - 检查约束:amount >= 0(金额非负) - 外键:department_id → departments.id - 设计要点:多维索引支持按科室、类型、类别、期间的高效查询;检查约束保证财务数据的正向性。 ```mermaid erEntity "department_finances" { id : integer PK department_id : integer FK period_year : integer period_month : integer finance_type : enum category : varchar(50) amount : numeric(12,2) created_at : datetime updated_at : datetime } ``` 图表来源 - [finance.py](file://backend/app/models/finance.py#L45-L74) 章节来源 - [finance.py](file://backend/app/models/finance.py#L45-L74) ## 依赖关系分析 - 模型层通过 __table_args__ 声明索引与约束,迁移层通过 Alembic 在数据库层面落地。 - 外键关系形成稳定的层次结构(如 departments 树形、assessments 与 staff 的自关联)。 - 初始化脚本在首次启动时创建表结构并注入基础数据。 ```mermaid graph TB subgraph "模型定义" MD["models.py"] MF["finance.py"] end subgraph "迁移执行" MI["001_initial.py"] MT["002_template.py"] end subgraph "运行时" DB["database.py"] CFG["config.py"] INIT["init_db.py"] end MD --> MI MF --> MT MI --> DB MT --> DB CFG --> DB INIT --> DB ``` 图表来源 - [models.py](file://backend/app/models/models.py#L62-L438) - [finance.py](file://backend/app/models/finance.py#L45-L79) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L21-L183) - [002_template.py](file://backend/alembic/versions/002_template.py#L21-L96) - [database.py](file://backend/app/core/database.py#L9-L39) - [config.py](file://backend/app/core/config.py#L18-L22) - [init_db.py](file://backend/init_db.py#L11-L83) 章节来源 - [models.py](file://backend/app/models/models.py#L62-L438) - [finance.py](file://backend/app/models/finance.py#L45-L79) - [001_initial.py](file://backend/alembic/versions/001_initial.py#L21-L183) - [002_template.py](file://backend/alembic/versions/002_template.py#L21-L96) - [database.py](file://backend/app/core/database.py#L9-L39) - [config.py](file://backend/app/core/config.py#L18-L22) - [init_db.py](file://backend/init_db.py#L11-L83) ## 性能考量 - 索引选择原则 - 单列索引:适用于等值/范围查询的过滤字段(如 status、dept_type、indicator_type)。 - 复合索引:适用于多条件组合查询(如 assessments 的 period_year + period_month,salary_records 的 period_year + period_month,finance 的 period_year + period_month)。 - 唯一索引:保证业务唯一性(code、employee_id、username、plan_code、模板-指标唯一组合)。 - 查询优化建议 - 使用复合索引覆盖多条件过滤,减少回表与排序开销。 - 对枚举字段建立索引,提升 IN/等值过滤效率。 - 尽量避免 SELECT *,仅选择必要列,降低 IO。 - 对大表的分页查询配合 ORDER BY + LIMIT,确保 ORDER BY 列命中索引。 - 并发控制 - 使用数据库事务隔离级别与锁策略,避免写放大与死锁。 - 对高并发写入场景,合理拆分热点表或引入分区(如按年/月分区)。 - 控制批量写入批次大小,避免长事务占用资源。 - 数据完整性 - 外键约束保证引用完整性;唯一约束保证业务唯一性;检查约束保证数值范围合法。 - 在 ORM 层与数据库层双重约束,确保数据一致性。 [本节为通用性能指导,不直接分析具体文件] ## 故障排查指南 - 索引未生效 - 检查查询条件是否与索引列顺序匹配(复合索引前缀匹配)。 - 使用 EXPLAIN/ANALYZE 分析执行计划,确认索引扫描路径。 - 唯一冲突 - 当插入重复唯一键时,捕获数据库异常并提示用户修正(如重复工号、重复指标编码)。 - 外键约束失败 - 确认被引用记录存在且状态有效;检查删除/更新策略(RESTRICT/CASCADE/SET NULL)。 - 检查约束失败 - 校验输入数据是否满足约束条件(如权重>0、金额>=0);在业务层提前校验,减少数据库往返。 [本节为通用排查建议,不直接分析具体文件] ## 结论 该系统在索引与约束设计上遵循“业务唯一性优先、高频查询覆盖、枚举与复合索引配合”的原则,结合外键与检查约束,有效保障了数据完整性与查询性能。建议在后续迭代中持续关注查询执行计划与热点表,适时引入分区与物化视图,进一步提升大规模数据下的响应能力。 [本节为总结性内容,不直接分析具体文件] ## 附录 - 数据库连接与池化配置 - 数据库 URL、连接池大小与溢出配置,确保高并发下的稳定性。 - 初始化脚本 - 首次启动自动创建表结构并注入基础数据,便于开发与测试环境快速就绪。 章节来源 - [config.py](file://backend/app/core/config.py#L18-L22) - [init_db.py](file://backend/init_db.py#L11-L83)