# 索引与约束设计
**本文引用的文件**
- [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)