摘要
医院HIS系统中,病区医嘱复核统计视图 VI_BQ_YWTX11 承担护士病区医嘱数据统计业务。该视图涉及多表关联查询,且WHERE条件包含大量 OR + IS NULL 组合判断,原始SQL执行耗费极高,按病区日期 BRBQ 查询频繁卡顿,严重影响医护人员日常操作效率。
本文基于Oracle 19c、千万级医嘱业务表真实生产环境,完整复盘该视图的调优全过程,涵盖复合索引踩坑、NULL值索引失效原理、SQL执行逻辑重构、真实性能校验等核心要点,适配医院运维、DBA及后端开发人员参考复用。
|----------------------------------------------------------|
| 生产环境 :Oracle 19c | 核心表EMR_YZB(千万级数据)| 医院HIS电子医嘱业务库 |
一、业务场景与性能问题
1.1 视图业务逻辑
VI_BQ_YWTX11 用于统计病区有效医嘱数量,适配普通住院患者、新生儿婴儿两类业务场景,核心表关联关系如下:
- 核心基表:EMR_YZB(医嘱主表)
- 关联基础表:医生接诊表 YS_ZY_JZJL、患者信息表 ZY_BRRY
- 特殊业务表:新生儿登记表BQ_XSEDJ(适配婴儿医嘱统计)
1.2 核心查询特征
- 业务查询90%以上基于病区日期 BRBQ 做范围筛选,日期检索为核心高频场景;
- 筛选条件存在大量OR 逻辑,且多字段依赖 IS NULL 判断医嘱状态;
- 原始SQL采用 多表JOIN后再GROUP BY 的执行逻辑。
1.3 线上性能现象
单天病区数据查询耗时久,执行计划Cost耗费数万,频繁触发 INDEX FAST FULL SCAN(索引快速全扫);跨多天、跨月大范围日期查询时,性能断崖式下跌,严重影响护士医嘱复核工作。
|----------------------------------------------------------------------------------------------------------|
| 重要知识点 :执行计划中的Cost(耗费)是Oracle优化器的相对权重估值,不是时间单位 ,不能作为真实耗时判断依据,真实性能必须通过set timing on 查看Elapsed执行时间。 |
二、初始优化踩坑:臃肿的全覆盖复合索引
2.1 错误索引方案
初期为实现索引全覆盖,将查询过滤、关联、分组、返回字段全部纳入复合索引KEY列,试图规避回表查询,提升效率:
|-------------------------------------------------------------------------------------------------|
| sql CREATE INDEX IDX_EMR_YZB_QUERY ON EMR_YZB(YZLX,YZZT,YZQX,XZJDGH,TZQRGH,ZYH,JGID,BRBQ,YEPB); |
该索引包含9个字段,直接导致索引体积臃肿、索引块数量激增,不仅没有提速,反而加剧性能问题,即便Oracle 19c优化器更智能,依然出现索引扫描低效问题。
2.2 核心问题根源
- B树索引NULL值限制 :Oracle B树索引不存储全NULL数据,XZJDGH IS NULL、TZQRGH IS NULL 等条件,无法被前置索引字段高效匹配,索引范围扫描失效;
- 索引臃肿反噬性能 :虽然Oracle 19c支持 INCLUDE 语法,但本次初始方案未使用,将所有字段强制写入索引key,索引体积过大,优化器判定索引扫描代价高于全表扫描,主动放弃索引范围扫描,选择索引全扫/表全扫;
- SQL执行逻辑缺陷 :先关联多表、后聚合分组,若 YS_ZY_JZJL 单住院号存在多条接诊记录,会产生笛卡尔积,大幅放大中间结果集,导致分组排序开销暴涨,同时出现医嘱统计数量虚高问题。
|-------------------------------------------------------------------------|
| 避坑总结 :Oracle 19c 即便支持索引覆盖,也切勿盲目堆砌索引KEY字段,冗余字段会大幅增加索引IO,废掉复合索引的查询优势。 |
三、索引重构:精简高效适配业务场景(19c最优方案)
摒弃全覆盖索引KEY设计,基于 Oracle 19c INCLUDE特性 ,遵循 等值过滤字段前置、关联字段居中、非检索字段INCLUDE覆盖 原则,重构核心索引,同时补齐关联表适配索引,兼顾查询效率与索引轻量化。
3.1 核心表索引优化(EMR_YZB 19c专属)
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| sql -- 删除老旧臃肿索引 DROP INDEX IDX_EMR_YZB_QUERY; -- 19c最优精简复合索引:等值筛选+关联字段为KEY,其余字段INCLUDE覆盖,轻量化无回表 CREATE INDEX IDX_EMR_YZB_QUERY ON EMR_YZB (YZLX,YZZT,ZYH) INCLUDE (JGID,BRBQ,YEPB); |
3.2 关联表配套索引优化
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| sql -- 医生接诊表:适配住院号关联查询 CREATE INDEX IDX_YS_ZY_JZJL_JZXH ON YS_ZY_JZJL(JZXH); -- 患者表:适配住院号+排除测评病人条件 CREATE INDEX IDX_ZY_BRRY_ZYH_CYPB ON ZY_BRRY(ZYH,CYPB); -- 新生儿业务表:适配婴儿医嘱关联逻辑 CREATE INDEX IDX_BQ_XSEDJ_YEWYH ON BQ_XSEDJ(YEWYH); CREATE INDEX IDX_BQ_XSEDJ_MQZYH ON BQ_XSEDJ(MQZYH); -- 刷新统计信息,19c自动统计信息收集可辅助,手动刷新确保精准 exec dbms_stats.gather_table_stats(USER,'EMR_YZB',cascade=>true); exec dbms_stats.gather_table_stats(USER,'YS_ZY_JZJL',cascade=>true); exec dbms_stats.gather_table_stats(USER,'ZY_BRRY',cascade=>true); |
3.3 索引优化效果
依托19c INCLUDE特性,索引KEY极致精简、体积大幅缩减,同时实现索引全覆盖,无回表开销。优化器可稳定命中 INDEX RANGE SCAN 索引范围扫描,基于BRBQ 单天/短期日期查询性能显著提升,彻底解决日常高频检索卡顿问题。
四、SQL核心逻辑重构:先聚合,后关联
原始SQL 多表JOIN→GROUP BY 的逻辑存在天然缺陷,多表关联产生的笛卡尔积会放大数据量,导致分组排序代价极高。本次优化核心思路:单表先聚合压缩数据,再做表关联。
先在核心医嘱表完成条件筛选、分组统计,压缩结果集行数后,再关联接诊表、患者表,从根源减少JOIN开销,同时解决统计数据虚高问题,且外层日期筛选条件可正常下推,精准命中索引。该逻辑适配Oracle 19c优化器,能最大化发挥索引优势。
优化后完整视图SQL
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| sql CREATE OR REPLACE VIEW VI_BQ_YWTX11 AS -- 普通住院患者医嘱统计:先聚合再关联 SELECT t.JGID, t.ZYH, t.BRBQ, t.YWSL, t.YEPB FROM ( SELECT JGID,ZYH,BRBQ,COUNT(*) AS YWSL,YEPB FROM EMR_YZB WHERE YZLX IN (0, 10) AND ( (YZZT = 1 AND XZJDGH IS NULL) OR (YZZT = 4 AND TZQRGH IS NULL) OR (YZZT = 3 AND ((YZQX = 1 AND TZQRGH IS NULL) OR (YZQX = 2 AND XZJDGH IS NULL))) ) GROUP BY JGID, ZYH, BRBQ, YEPB ) t INNER JOIN YS_ZY_JZJL jl ON t.ZYH = jl.JZXH INNER JOIN ZY_BRRY br ON jl.JZXH = br.ZYH AND br.CYPB = 0 UNION ALL -- 新生儿婴儿医嘱统计:先聚合再关联 SELECT t.JGID, t.ZYH, t.BRBQ, t.YWSL, t.YEPB FROM ( SELECT JGID,ZYH,BRBQ,COUNT(*) AS YWSL,YEPB FROM EMR_YZB WHERE YZLX IN (0, 10) AND ( (YZZT = 1 AND XZJDGH IS NULL) OR (YZZT = 4 AND TZQRGH IS NULL AND CZBZ = 0) OR (YZZT = 3 AND ((YZQX = 1 AND TZQRGH IS NULL) OR (YZQX = 2 AND XZJDGH IS NULL))) ) GROUP BY JGID, ZYH, BRBQ, YEPB ) t INNER JOIN BQ_XSEDJ x ON x.YEWYH = t.ZYH INNER JOIN YS_ZY_JZJL jl ON x.MQZYH = jl.JZXH INNER JOIN ZY_BRRY br ON jl.JZXH = br.ZYH AND br.CYPB = 0; |
同时保留 YS_ZY_JZJL 医生接诊表关联逻辑,满足护士医嘱复核仅统计已接诊患者医嘱的业务要求,兼顾性能与业务准确性。
五、性能校验与验证标准
5.1 真实耗时校验(核心)
摒弃执行计划Cost估值,通过真实语句校验查询速度:
|----------------------------------------------------------------------------------------------------------------|
| sql set timing on; -- 模拟日常病区日期查询 select * from VI_BQ_YWTX11 where brbq between '2026-08-30' and '2026-08-31'; |
通过 Elapsed 时间精准判断优化效果,优化后日常单日期查询可实现秒级响应。
5.2 执行计划校验
- ✅ 小范围日期查询:稳定命中 INDEX RANGE SCAN 索引范围扫描,性能最优;
- ⚠️ 大范围跨月查询:自动切换INDEX FAST FULL SCAN,属于正常现象,超大批量数据查询索引优势有限。
5.3 业务数据校验
对比优化前后视图返回行数、医嘱统计数量,确保SQL改写、索引调整零业务偏差,无统计失真、数据丢失问题。
六、生产调优总结(可直接复用)
- 19c索引最优实践 :充分利用19c INCLUDE 特性,过滤、关联字段放索引KEY,展示、分组字段INCLUDE覆盖,实现轻量化索引全覆盖,避免索引臃肿;
- NULL值索引原则 :B树索引不适配NULL值前置筛选,含大量 IS NULL 条件的字段,禁止放在复合索引前导列;
- 统计类SQL最优逻辑 :多表聚合统计场景,优先单表先聚合压缩数据,后多表关联,从根源规避笛卡尔积数据膨胀;
- 性能判断标准 :严格区分Cost估值与真实执行时间,以 set timing on实际耗时为唯一优化依据;
- HIS索引规范:医疗业务表读写频繁,禁止泛滥建索引,19c借助INCLUDE特性可大幅降低索引维护开销,兼顾查询性能与DML写入压力。
|-------------------------------------------------------------------------------------------|
| 拓展优化方案 :若极端场景下OR条件性能仍不佳,可将多段OR逻辑拆分为多个 UNION ALL 独立查询,每段单独命中索引,进一步提升性能,代价为SQL维护成本小幅增加。 |
文章标签
#Oracle19c#HIS数据库调优#SQL视图优化#复合索引踩坑#先聚合再关联#IS NULL索引问题