22 KiB
数据库约束设计
**本文档引用的文件** - [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)目录
简介
本文件详细阐述了医院绩效管理系统的数据库约束设计,涵盖唯一性约束、检查约束、索引设计和外键约束的实现原理与业务意义。通过对系统中各个数据表的约束策略进行深入分析,帮助开发者和运维人员理解数据完整性保障机制,并提供针对约束冲突的处理策略和优化建议。
项目结构
该系统采用基于 SQLAlchemy 的 ORM 设计,数据库约束主要通过以下方式实现:
- 数据模型层定义:在模型类中直接声明约束和索引
- 迁移脚本层:通过 Alembic 版本化管理数据库结构变更
- 配置层:数据库连接和会话管理
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
图表来源
章节来源
核心组件
系统的核心数据模型包括科室、员工、考核指标、考核记录、工资记录等关键实体。每个实体都配备了相应的约束策略以确保数据完整性。
主要数据实体
| 实体名称 | 主键字段 | 唯一约束 | 外键约束 | 检查约束 |
|---|---|---|---|---|
| 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 |
章节来源
架构概览
系统采用三层约束设计架构:
graph TB
subgraph "约束层次"
Layer1[业务层约束<br/>模型定义]
Layer2[持久层约束<br/>数据库层面]
Layer3[应用层约束<br/>应用程序逻辑]
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
图表来源
详细组件分析
唯一性约束设计
唯一性约束是确保数据唯一性的基础机制,系统在多个关键字段上实施了唯一性约束。
唯一性约束实现
| 表名 | 唯一字段 | 约束目的 | 业务意义 |
|---|---|---|---|
| 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) | 关联关系唯一性 | 防止重复关联,确保模板-指标关系的准确性 |
唯一性约束触发条件
flowchart TD
Start([数据插入/更新]) --> CheckUnique["检查唯一性约束"]
CheckUnique --> UniqueExists{"是否存在重复值?"}
UniqueExists --> |是| RaiseError["抛出唯一性约束异常"]
UniqueExists --> |否| Continue["继续执行操作"]
RaiseError --> HandleConflict["处理冲突"]
HandleConflict --> Rollback["回滚事务"]
Continue --> Commit["提交事务"]
Rollback --> End([结束])
Commit --> End
图表来源
章节来源
检查约束设计
检查约束用于强制执行复杂的业务规则,确保数据符合预定义的条件。
检查约束实现
| 表名 | 约束条件 | 约束名称 | 业务含义 |
|---|---|---|---|
| indicators | weight > 0 | ck_indicator_weight | 确保指标权重必须为正数,保证计算逻辑正确性 |
| department_finances | amount >= 0 | ck_finance_amount | 确保财务金额不能为负数,维护财务数据准确性 |
检查约束验证流程
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
图表来源
章节来源
外键约束设计
外键约束确保参照完整性,防止出现孤立记录和无效引用。
外键约束实现
| 表名 | 外键字段 | 引用表 | 约束类型 | 业务意义 |
|---|---|---|---|---|
| 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 | 必须引用有效科室 | 确保财务记录对应有效科室 |
外键约束级联行为
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"
图表来源
章节来源
索引设计策略
索引设计直接影响查询性能和数据检索效率,系统针对高频查询场景建立了多维索引。
单列索引设计
| 表名 | 索引字段 | 索引名称 | 查询场景 | 性能收益 |
|---|---|---|---|---|
| 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 | 按类别查询财务记录 | 快速筛选特定类别 |
复合索引设计思路
复合索引通过组合多个字段来优化特定查询模式:
flowchart TD
QueryPattern[查询模式分析] --> IdentifyFields["识别查询字段"]
IdentifyFields --> Selectivity["评估选择性"]
Selectivity --> OrderFields["确定字段顺序"]
OrderFields --> TestPerformance["测试查询性能"]
TestPerformance --> CreateIndex["创建复合索引"]
CreateIndex --> MonitorUsage["监控索引使用"]
MonitorUsage --> OptimizeIndex["优化索引设计"]
subgraph "复合索引示例"
PeriodIndex["idx_assessment_period<br/>(year, month)"]
RelationIndex["idx_ti_unique<br/>(template_id, indicator_id)"]
PlanIndex["idx_plan_department<br/>(department_id, status)"]
end
图表来源
章节来源
约束冲突处理策略
当数据库约束被违反时,系统需要有一套完善的冲突处理机制。
错误处理流程
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
约束冲突类型及处理
| 冲突类型 | 触发条件 | 处理策略 | 用户反馈 |
|---|---|---|---|
| 唯一性冲突 | 插入重复唯一值 | 提示用户修改唯一字段值 | "该[字段]已存在,请使用其他值" |
| 外键冲突 | 引用不存在的外键值 | 提示用户选择有效引用值 | "请选择有效的[关联实体]" |
| 检查约束冲突 | 数据不符合检查条件 | 提示用户修正数据格式 | "[字段]必须满足[条件说明]" |
| 级联删除冲突 | 尝试删除有子记录的父记录 | 提示用户先删除子记录或调整级联策略 | "请先删除子记录或调整关联关系" |
章节来源
依赖关系分析
系统约束设计涉及多个层面的依赖关系,形成了完整的数据完整性保障体系。
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
图表来源
章节来源
性能考虑
合理的约束设计不仅保证数据完整性,还能显著提升系统性能。
索引性能优化
查询性能分析
| 查询类型 | 索引使用情况 | 性能影响 | 优化建议 |
|---|---|---|---|
| 等值查询 | 使用精确匹配索引 | 高性能 | 确保查询条件包含索引字段 |
| 范围查询 | 使用范围扫描索引 | 中等性能 | 优化查询条件,减少扫描范围 |
| 复合查询 | 使用复合索引 | 高性能 | 确保查询条件覆盖索引前缀 |
| 排序查询 | 使用排序索引 | 高性能 | 确保ORDER BY字段包含在索引中 |
| 连接查询 | 使用连接索引 | 高性能 | 确保JOIN字段建立索引 |
索引维护策略
flowchart TD
Monitor[监控索引使用] --> AnalyzeUsage["分析索引使用率"]
AnalyzeUsage --> IdentifyUnused["识别未使用索引"]
IdentifyUnused --> DropUnused["删除无用索引"]
AnalyzeUsage --> IdentifyMissing["识别缺失索引"]
IdentifyMissing --> CreateIndex["创建必要索引"]
CreateIndex --> Monitor
DropUnused --> Monitor
Monitor --> RebuildIndex["重建碎片索引"]
RebuildIndex --> Monitor
约束对性能的影响
| 约束类型 | 正面影响 | 负面影响 | 性能平衡点 |
|---|---|---|---|
| 唯一性约束 | 数据一致性保证 | 插入/更新时额外检查 | 在高频写入场景下适度放宽 |
| 检查约束 | 业务规则强制执行 | 数据验证开销 | 复杂检查约束需要谨慎使用 |
| 外键约束 | 参照完整性保障 | 关联查询性能影响 | 对频繁关联查询考虑性能权衡 |
| 索引约束 | 查询性能提升 | 写入性能下降 | 基于查询模式优化索引设计 |
故障排除指南
常见约束问题诊断
唯一性约束冲突
问题症状:插入或更新数据时报唯一性约束错误
诊断步骤:
- 检查冲突的具体字段和值
- 验证是否存在重复数据
- 确认业务逻辑是否允许重复
- 检查数据清理流程
解决方案:
- 修改唯一字段值
- 清理重复数据
- 调整业务逻辑
- 重新设计唯一性策略
外键约束冲突
问题症状:引用不存在的数据时报外键约束错误
诊断步骤:
- 确认被引用表中是否存在对应记录
- 检查关联字段的数据类型和格式
- 验证数据插入顺序
- 检查是否有级联删除策略
解决方案:
- 先插入父记录再插入子记录
- 使用正确的关联值
- 调整级联删除策略
- 实施数据预校验
检查约束冲突
问题症状:数据不符合检查条件时报错
诊断步骤:
- 检查具体违反的约束条件
- 验证数据格式和范围
- 确认业务规则合理性
- 检查数据转换逻辑
解决方案:
- 修正数据格式
- 调整业务规则
- 实施数据验证
- 优化约束条件
数据库迁移问题
迁移失败处理
常见问题:
- 字段已存在导致迁移失败
- 索引创建冲突
- 外键约束依赖问题
处理策略:
- 检查当前数据库状态
- 手动执行必要的DDL操作
- 调整迁移脚本顺序
- 实施回滚和重试机制
章节来源
结论
本系统的数据库约束设计体现了多层次、全方位的数据完整性保障机制。通过合理运用唯一性约束、检查约束、外键约束和索引约束,系统在保证数据准确性和一致性的前提下,实现了高效的查询性能和良好的扩展性。
设计亮点
- 层次化约束设计:从模型层到数据库层的完整约束覆盖
- 业务导向的约束策略:每个约束都有明确的业务意义和触发条件
- 性能优化的索引设计:针对高频查询场景的复合索引策略
- 完善的冲突处理机制:从技术层面到用户体验的全面考虑
持续改进建议
- 监控和分析:建立约束使用情况的监控机制
- 定期优化:根据查询模式变化调整索引策略
- 文档完善:持续更新约束设计文档和最佳实践
- 性能测试:定期进行约束对性能影响的评估测试
通过这套完整的约束设计体系,系统能够可靠地支撑医院绩效管理的各项业务需求,为医疗质量管理提供坚实的数据基础。