# 索引与约束设计
**本文引用的文件**
- [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)
- [env.py](file://backend/alembic/env.py)
- [init_db.py](file://backend/init_db.py)
- [database.md](file://docs/database.md)
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概览](#架构概览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考量](#性能考量)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本文件聚焦于该医院绩效系统的数据库索引与约束设计,系统性梳理各表的索引策略(单列、复合、唯一)、约束实现(主键、外键、检查、唯一),并结合业务场景给出性能分析、查询优化建议、约束对数据完整性的作用以及索引维护与监控建议。内容严格基于仓库中的模型定义、迁移脚本与文档,避免臆测。
## 项目结构
系统采用 SQLAlchemy ORM + Alembic 迁移的典型后端架构,数据库层通过异步引擎连接,模型文件集中定义表结构、索引与约束,迁移脚本负责版本演进。
```mermaid
graph TB
subgraph "数据库层"
Engine["异步引擎
engine"]
Session["会话工厂
async_session_maker"]
Base["ORM基类
Base"]
end
subgraph "模型层"
Models["模型定义
models.py"]
Finance["财务模型
finance.py"]
end
subgraph "迁移层"
Env["Alembic环境
env.py"]
V001["初始迁移
001_initial.py"]
V002["模板迁移
002_template.py"]
end
Engine --> Session
Session --> Base
Base --> Models
Base --> Finance
Env --> V001
Env --> V002
```
图表来源
- [database.py](file://backend/app/core/database.py#L10-L20)
- [models.py](file://backend/app/models/models.py#L1-L20)
- [finance.py](file://backend/app/models/finance.py#L1-L20)
- [env.py](file://backend/alembic/env.py#L10-L18)
- [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#L1-L39)
- [env.py](file://backend/alembic/env.py#L1-L66)
## 核心组件
- 异步数据库引擎与会话工厂:负责连接与事务管理,支持并发读写。
- ORM模型基类:统一模型继承,承载索引与约束声明。
- 模型定义:集中定义表结构、字段类型、索引与约束。
- Alembic迁移:版本化管理数据库结构变更,确保团队协作一致性。
章节来源
- [database.py](file://backend/app/core/database.py#L10-L20)
- [models.py](file://backend/app/models/models.py#L1-L20)
- [finance.py](file://backend/app/models/finance.py#L1-L15)
- [env.py](file://backend/alembic/env.py#L10-L18)
## 架构概览
系统通过 Alembic 将模型定义映射为数据库表结构,并在迁移脚本中显式声明索引与约束。模型层使用 SQLAlchemy 的 Index 与 CheckConstraint 等机制,迁移层通过 op.create_index/op.create_table 等操作同步到数据库。
```mermaid
sequenceDiagram
participant Dev as "开发者"
participant Alembic as "Alembic"
participant Env as "env.py"
participant Conn as "数据库连接"
participant DB as "数据库"
Dev->>Alembic : 生成/执行迁移
Alembic->>Env : 加载目标元数据
Env->>Conn : 获取异步连接
Conn->>DB : 执行DDL建表/索引/约束
DB-->>Conn : 返回结果
Conn-->>Alembic : 提交/回滚
Alembic-->>Dev : 迁移完成
```
图表来源
- [env.py](file://backend/alembic/env.py#L42-L53)
- [001_initial.py](file://backend/alembic/versions/001_initial.py#L21-L183)
- [002_template.py](file://backend/alembic/versions/002_template.py#L21-L96)
## 详细组件分析
### 员工信息表(staff)
- 主键:id(自增)
- 唯一约束:employee_id(工号唯一)
- 外键:department_id → departments.id
- 单列索引:idx_staff_dept(department_id)、idx_staff_status(status)
- 复合索引:无
- 检查约束:无
- 设计要点
- 员工工号唯一,便于跨模块引用与去重。
- 员工状态与所属科室是高频过滤条件,分别建立单列索引以提升 WHERE 查询效率。
- 与部门的多对一关系通过外键约束保证引用完整性。
章节来源
- [models.py](file://backend/app/models/models.py#L88-L114)
- [001_initial.py](file://backend/alembic/versions/001_initial.py#L42-L64)
### 科室信息表(departments)
- 主键:id(自增)
- 唯一约束:code(科室编码唯一)
- 外键:parent_id → departments.id(自关联,树形结构)
- 单列索引:idx_dept_type(dept_type)、idx_dept_parent(parent_id)
- 复合索引:无
- 检查约束:无
- 设计要点
- 科室编码唯一,支撑业务识别与报表统计。
- 科室类型与层级关系常用于筛选与聚合,单列索引覆盖常见查询模式。
- 自关联外键支持组织架构树的层次遍历与父子关系校验。
章节来源
- [models.py](file://backend/app/models/models.py#L62-L86)
- [001_initial.py](file://backend/alembic/versions/001_initial.py#L22-L41)
### 考核记录表(assessments)
- 主键:id(自增)
- 外键:staff_id → staff.id;assessor_id/reviewer_id → staff.id(自关联)
- 单列索引:idx_assessment_staff(staff_id)、idx_assessment_status(status)
- 复合索引:idx_assessment_period(period_year, period_month)
- 检查约束:无
- 设计要点
- 考核周期复合索引覆盖“按年月统计”等典型查询。
- 员工与状态索引满足“某员工某状态”的快速检索。
- 多个自关联外键确保考核流程的完整性。
章节来源
- [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 → assessments.id;indicator_id → indicators.id
- 单列索引:idx_detail_assessment(assessment_id)、idx_detail_indicator(indicator_id)
- 复合索引:无
- 检查约束:无
- 设计要点
- 明细表按考核记录与指标分别建立单列索引,支撑“查看某考核的所有指标”和“查看某指标在所有考核中的表现”。
章节来源
- [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 → staff.id
- 单列索引:idx_salary_staff(staff_id)
- 复合索引:idx_salary_period(period_year, period_month)
- 检查约束:无
- 设计要点
- 工资按员工与周期查询频繁,分别建立单列与复合索引以优化检索与聚合。
章节来源
- [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(可选关联)
- 单列索引:idx_user_username(username)
- 复合索引:无
- 检查约束:无
- 设计要点
- 登录凭据唯一性由用户名唯一约束保证,索引加速登录与鉴权查询。
章节来源
- [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(计划编码唯一)
- 外键:department_id → departments.id;staff_id → staff.id;submitter_id/approver_id → users.id;parent_plan_id → performance_plans.id(自关联)
- 单列索引:idx_plan_level(plan_level)、idx_plan_year(plan_year)、idx_plan_department(department_id)、idx_plan_status(status)
- 复合索引:无
- 检查约束:无
- 设计要点
- 计划层级、年份、状态与部门是常见的过滤维度,单列索引覆盖主要查询场景。
章节来源
- [models.py](file://backend/app/models/models.py#L270-L311)
### 计划-指标关联表(plan_kpi_relations)
- 主键:id(自增)
- 外键:plan_id → performance_plans.id;indicator_id → indicators.id
- 单列索引:idx_relation_plan(plan_id)、idx_relation_indicator(indicator_id)
- 复合索引:idx_relation_unique(plan_id, indicator_id, unique=True)
- 检查约束:无
- 设计要点
- 关联表的复合唯一索引确保“同一计划下的同一指标仅出现一次”,避免重复绑定。
章节来源
- [models.py](file://backend/app/models/models.py#L314-L338)
### 指标模板表(indicator_templates)
- 主键:id(自增)
- 唯一约束:template_code(模板编码唯一)
- 单列索引:idx_template_type(template_type)、idx_template_active(is_active)
- 复合索引:无
- 检查约束:无
- 设计要点
- 模板类型与启用状态是筛选热点,单列索引满足模板选择与激活状态查询。
章节来源
- [models.py](file://backend/app/models/models.py#L387-L408)
- [002_template.py](file://backend/alembic/versions/002_template.py#L22-L39)
### 模板-指标关联表(template_indicators)
- 主键:id(自增)
- 外键:template_id → indicator_templates.id;indicator_id → indicators.id
- 单列索引:idx_ti_template(template_id)、idx_ti_indicator(indicator_id)
- 复合索引:idx_ti_unique(template_id, indicator_id, unique=True)
- 检查约束:无
- 设计要点
- 关联表的复合唯一索引确保“同一模板下的同一指标仅出现一次”,避免重复绑定。
章节来源
- [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 → departments.id
- 单列索引:idx_finance_dept(department_id)、idx_finance_period(period_year, period_month)、idx_finance_type(finance_type)、idx_finance_category(category)
- 复合索引:无
- 检查约束:ck_finance_amount(amount >= 0)
- 设计要点
- 财务按科室、周期、类型与类别查询频繁,单列索引覆盖这些过滤维度。
- 金额非负的检查约束保证财务数据的数值正确性。
章节来源
- [finance.py](file://backend/app/models/finance.py#L45-L74)
### 考核指标表(indicators)
- 主键:id(自增)
- 唯一约束:code(指标编码唯一)
- 单列索引:idx_indicator_type(indicator_type)
- 复合索引:无
- 检查约束:ck_indicator_weight(weight > 0)
- 设计要点
- 指标类型索引满足按类型筛选与统计。
- 权重正数检查约束确保指标权重的合理性。
章节来源
- [models.py](file://backend/app/models/models.py#L117-L146)
- [001_initial.py](file://backend/alembic/versions/001_initial.py#L66-L85)
## 依赖关系分析
```mermaid
erDiagram
DEPARTMENTS {
int id PK
string code UK
string dept_type
int parent_id FK
}
STAFF {
int id PK
string employee_id UK
int department_id FK
string status
}
ASSESSMENTS {
int id PK
int staff_id FK
int period_year
int period_month
string status
}
ASSESSMENT_DETAILS {
int id PK
int assessment_id FK
int indicator_id FK
}
SALARY_RECORDS {
int id PK
int staff_id FK
int period_year
int period_month
}
USERS {
int id PK
string username UK
}
PERFORMANCE_PLANS {
int id PK
string plan_code UK
string plan_level
int department_id FK
string status
}
PLAN_KPI_RELATIONS {
int id PK
int plan_id FK
int indicator_id FK
}
INDICATOR_TEMPLATES {
int id PK
string template_code UK
string template_type
boolean is_active
}
TEMPLATE_INDICATORS {
int id PK
int template_id FK
int indicator_id FK
}
DEPARTMENT_FINANCES {
int id PK
int department_id FK
int period_year
int period_month
string finance_type
string category
}
INDICATORS {
int id PK
string code UK
string indicator_type
numeric weight
}
DEPARTMENTS ||--o{ STAFF : "1:N"
STAFF ||--o{ ASSESSMENTS : "1:N"
ASSESSMENTS ||--o{ ASSESSMENT_DETAILS : "1:N"
STAFF ||--o{ SALARY_RECORDS : "1:N"
DEPARTMENTS ||--o{ DEPARTMENT_FINANCES : "1:N"
INDICATORS ||--o{ ASSESSMENT_DETAILS : "1:N"
INDICATORS ||--o{ PLAN_KPI_RELATIONS : "1:N"
INDICATORS ||--o{ TEMPLATE_INDICATORS : "1:N"
INDICATOR_TEMPLATES ||--o{ TEMPLATE_INDICATORS : "1:N"
PERFORMANCE_PLANS ||--o{ PLAN_KPI_RELATIONS : "1:N"
USERS ||--o{ PERFORMANCE_PLANS : "1:N"
```
图表来源
- [models.py](file://backend/app/models/models.py#L62-L437)
- [finance.py](file://backend/app/models/finance.py#L45-L74)
- [001_initial.py](file://backend/alembic/versions/001_initial.py#L22-L183)
- [002_template.py](file://backend/alembic/versions/002_template.py#L22-L96)
## 性能考量
- 索引策略与查询模式匹配
- 员工与科室:按部门与状态过滤频繁,单列索引 idx_staff_dept、idx_staff_status、idx_dept_type、idx_dept_parent 能有效降低扫描范围。
- 考核与工资:按员工与周期查询最常见,单列索引 idx_assessment_staff、idx_salary_staff,以及复合索引 idx_assessment_period、idx_salary_period 覆盖“某员工某周期”的查询。
- 模板与计划:按类型、状态、启用状态过滤,单列索引 idx_template_type、idx_template_active、idx_plan_level、idx_plan_year、idx_plan_department、idx_plan_status。
- 财务:按科室、周期、类型、类别过滤,单列索引 idx_finance_dept、idx_finance_period、idx_finance_type、idx_finance_category。
- 复合索引选择
- 考核与工资的“年+月”组合索引能显著提升范围查询与分组统计性能。
- 关联表的“计划+指标”复合唯一索引避免重复绑定,同时作为唯一约束提升查询稳定性。
- 检查约束
- 指标权重与财务金额的非负检查约束在写入阶段即拦截异常数据,减少后续修复成本。
- 查询优化建议
- 对高频过滤字段(如 status、dept_type、finance_type)优先使用单列索引。
- 对范围查询(如 period_year/period_month)优先使用复合索引。
- 避免 SELECT *,明确字段以减少 IO。
- 对大表分页查询使用 LIMIT/OFFSET 或基于游标的分页策略,避免深度分页导致的性能退化。
- 索引维护与监控
- 定期分析表与索引统计信息,识别未使用或冗余索引。
- 监控慢查询日志,定位缺失索引的查询模式并补充索引。
- 在高并发写入场景下,评估索引数量对写入性能的影响,必要时调整索引策略。
## 故障排查指南
- 索引缺失导致的慢查询
- 症状:WHERE/JOIN/ORDER BY/聚合查询耗时长。
- 排查:确认相关字段是否具备单列或复合索引;检查查询执行计划。
- 处置:根据查询模式新增索引或调整现有索引顺序。
- 唯一约束冲突
- 症状:插入/更新时报唯一约束冲突。
- 排查:确认唯一字段(如 employee_id、code、username、plan_code、template_code)是否重复。
- 处置:修正数据或调整业务逻辑,避免重复提交。
- 外键约束失败
- 症状:插入/更新时报外键约束错误。
- 排查:确认被引用表是否存在对应记录;确认字段类型与长度一致。
- 处置:先创建被引用记录,再进行关联写入。
- 检查约束触发
- 症状:插入/更新时报检查约束错误(如权重非正、金额为负)。
- 排查:核对业务输入是否符合约束条件。
- 处置:修正业务逻辑或前端校验,确保数据合法。
- 迁移与版本问题
- 症状:迁移失败或版本不一致。
- 排查:检查 Alembic 版本历史与当前数据库状态;确认迁移脚本语法正确。
- 处置:使用 Alembic 命令检查/回滚/升级至目标版本。
章节来源
- [env.py](file://backend/alembic/env.py#L42-L53)
- [001_initial.py](file://backend/alembic/versions/001_initial.py#L21-L183)
- [002_template.py](file://backend/alembic/versions/002_template.py#L21-L96)
## 结论
本系统在模型层与迁移层均明确声明了索引与约束,覆盖了业务高频查询与数据完整性需求。通过单列与复合索引的合理布局,配合外键与检查约束,既保证了查询性能,也强化了数据一致性。建议持续监控查询性能与索引使用情况,动态优化索引策略以适应业务增长。
## 附录
### 索引与约束清单(按表)
- departments
- 唯一:code
- 外键:parent_id → departments.id
- 单列索引:idx_dept_type、idx_dept_parent
- staff
- 唯一:employee_id
- 外键:department_id → departments.id
- 单列索引:idx_staff_dept、idx_staff_status
- indicators
- 唯一:code
- 单列索引:idx_indicator_type
- 检查约束:weight > 0
- assessments
- 外键:staff_id → staff.id;assessor_id/reviewer_id → staff.id
- 单列索引:idx_assessment_staff、idx_assessment_status
- 复合索引:idx_assessment_period
- assessment_details
- 外键:assessment_id → assessments.id;indicator_id → indicators.id
- 单列索引:idx_detail_assessment、idx_detail_indicator
- salary_records
- 外键:staff_id → staff.id
- 单列索引:idx_salary_staff
- 复合索引:idx_salary_period
- users
- 唯一:username
- 外键:staff_id → staff.id
- 单列索引:idx_user_username
- performance_plans
- 唯一:plan_code
- 外键:department_id → departments.id;staff_id → staff.id;submitter_id/approver_id → users.id;parent_plan_id → performance_plans.id
- 单列索引:idx_plan_level、idx_plan_year、idx_plan_department、idx_plan_status
- plan_kpi_relations
- 外键:plan_id → performance_plans.id;indicator_id → indicators.id
- 单列索引:idx_relation_plan、idx_relation_indicator
- 复合唯一索引:idx_relation_unique
- indicator_templates
- 唯一:template_code
- 单列索引:idx_template_type、idx_template_active
- template_indicators
- 外键:template_id → indicator_templates.id;indicator_id → indicators.id
- 单列索引:idx_ti_template、idx_ti_indicator
- 复合唯一索引:idx_ti_unique
- department_finances
- 外键:department_id → departments.id
- 单列索引:idx_finance_dept、idx_finance_period、idx_finance_type、idx_finance_category
- 检查约束:amount >= 0
章节来源
- [models.py](file://backend/app/models/models.py#L62-L437)
- [finance.py](file://backend/app/models/finance.py#L45-L74)
- [001_initial.py](file://backend/alembic/versions/001_initial.py#L22-L183)
- [002_template.py](file://backend/alembic/versions/002_template.py#L22-L96)