窗口函数不直接检测异常值,但通过percentile_cont、lag、avg等结合分组排序构建上下文,再依业务规则过滤实现高效异常识别。

窗口函数本身不直接检测或修正异常值,但能高效支撑异常识别逻辑——关键在于用 PERCENTILE_CONT、LAG、AVG 等配合分组与排序构建上下文,再结合业务规则过滤。
用 PERCENTILE_CONT 计算分位数阈值(比如剔除门诊量 Top 1% 的极端值)
医疗指标如单日门诊量、平均住院时长常呈偏态分布,硬设固定阈值(如 >1000 就是异常)容易误杀。更稳妥的是按科室+日期粒度动态算分位数。
-
PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY visit_count)必须搭配GROUP BY dept_id, visit_date,否则全表计算失去业务意义 - PostgreSQL 和 SQL Server 支持该语法;MySQL 8.0+ 需改用
PERCENTILE_DISC或子查询模拟,且注意PERCENTILE_CONT返回DOUBLE类型,比较前要显式转换 - 示例:筛选某科室当日门诊量超过该科室近30天第99百分位的记录:
SELECT dept_id, visit_date, visit_count<br>FROM (SELECT dept_id, visit_date, visit_count,<br> PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY visit_count)<br> OVER (PARTITION BY dept_id ORDER BY visit_date ROWS BETWEEN 29 PRECEDING AND CURRENT ROW) AS p99_30d<br> FROM outpatient_log) t<br>WHERE visit_count > p99_30d;
用 LAG/LEAD 检测突变型异常(如护士排班连续两天超16小时)
这类异常依赖时间序列的相邻关系,窗口函数比自连接更简洁安全。
-
LAG(work_hours, 1) OVER (PARTITION BY nurse_id ORDER BY shift_date)中ORDER BY必须明确,否则顺序不可控;若存在同日多班次,需补上shift_start_time避免并列排序歧义 - 空值处理要主动:第一次排班没有前序记录,
LAG返回 NULL,直接> 16判断会漏掉;应写成work_hours - COALESCE(LAG(work_hours) ..., 0) > 8或用IS NOT NULL过滤 - 注意时区:若数据跨时区采集,
shift_date应统一转为本地标准时间再排序,否则LAG取到的可能是隔壁省的班次
用 AVG + STDDEV 做 Z-score 式离群判断(适合检验科结果复核场景)
当某检验项目(如肌酐值)需快速标记偏离群体均值超2个标准差的样本,窗口聚合比先算全局统计再关联更省内存。
-
AVG(result_value) OVER (PARTITION BY test_item, lab_batch_id)—— 分批次计算均值,避免不同仪器校准差异干扰 -
STDDEV(result_value)在小样本(COUNT(*) > 4 过滤;PostgreSQL 中STDDEV_SAMP比STDDEV_POP更合适 - Z-score 公式别硬编码:直接写
(result_value - AVG(result_value) ...) / NULLIF(STDDEV(result_value) ..., 0),NULLIF防止标准差为0导致除零错误
真正难的不是写出窗口函数,而是把医疗业务规则翻译成可分组、可排序、可滑动的计算单元——比如“同一患者7天内重复开同一抗生素”需要 PARTITION BY patient_id ORDER BY prescribe_time,但还要处理处方时间精度到秒、不同医嘱系统时间戳偏差等问题。这些细节不落在 SQL 里,而藏在前期的数据清洗和业务对齐中。











