Oracle SQL按人员和指标筛选至首个异常行的实现方案
Oracle SQL 数据筛选实现:保留至首个异常记录(含该行)
我们有存储人员车辆时序数据的表,指标上报频率分日/月/季度,已通过LAG()和CASE生成了Status字段(OK表示上报合规,not OK表示异常)。现在需要按**人员(Person)+指标(Indicator)**分组筛选数据:
- 保留每组中从第一条到首个
Status='not OK'的所有行(包含该行) - 若组内无异常,则保留全部行
原始数据
| Indicator | Date | Prev_Date | Frequency | row_num | Status |
|---|---|---|---|---|---|
| km | 2024/04/03 | 2024/04/02 | Daily | 1 | OK |
| km | 2024/04/02 | 2024/04/01 | Daily | 2 | OK |
| km | 2024/04/01 | 2024/01/31 | Daily | 3 | not OK |
| km | 2024/01/31 | 2024/01/30 | Daily | 4 | not OK |
| gas in l | 2024/04/01 | 2024/01/01 | Quarterly | 1 | OK |
| gas in l | 2024/01/01 | 2023/01/01 | Quarterly | 2 | not OK |
| gas in l | 2023/01/01 | 2022/10/01 | Quarterly | 3 | OK |
| km | 2024/04/03 | 2024/04/02 | Daily | 1 | OK |
| km | 2024/04/02 | 2024/04/01 | Daily | 2 | OK |
| km | 2024/04/01 | 2024/03/31 | Daily | 3 | OK |
| km | 2024/03/31 | None | Daily | 4 | not OK |
| gas in l | 2024/04/01 | 2024/01/01 | Quarterly | 1 | OK |
| gas in l | 2024/01/01 | 2023/01/01 | Quarterly | 2 | not OK |
| gas in l | 2023/01/01 | 2022/10/01 | Quarterly | 3 | OK |
期望输出
| Indicator | Date | Prev_Date | Frequency | row_num | Status |
|---|---|---|---|---|---|
| km | 2024/04/03 | 2024/04/02 | Daily | 1 | OK |
| km | 2024/04/02 | 2024/04/01 | Daily | 2 | OK |
| km | 2024/04/01 | 2024/01/31 | Daily | 3 | not OK |
| gas in l | 2024/04/01 | 2024/01/01 | Quarterly | 1 | OK |
| gas in l | 2024/01/01 | 2023/01/01 | Quarterly | 2 | not OK |
| km | 2024/04/03 | 2024/04/02 | Daily | 1 | OK |
| km | 2024/04/02 | 2024/04/01 | Daily | 2 | OK |
| km | 2024/04/01 | 2024/03/31 | Daily | 3 | OK |
| km | 2024/03/31 | None | Daily | 4 | not OK |
| gas in l | 2024/04/01 | 2024/01/01 | Quarterly | 1 | OK |
| gas in l | 2024/01/01 | 2023/01/01 | Quarterly | 2 | not OK |
解决方案SQL
方法1:定位首个异常行号筛选
通过窗口函数找到每个分组内首个异常记录的row_num,再筛选行号不超过该值的记录:
WITH ranked_data AS ( SELECT t.*, MIN(CASE WHEN Status = 'not OK' THEN row_num ELSE NULL END) OVER (PARTITION BY Person, Indicator) AS first_error_row FROM your_table t ) SELECT Person, Indicator, Date, Prev_Date, Frequency, row_num, Status FROM ranked_data WHERE row_num <= NVL( first_error_row, (SELECT MAX(row_num) FROM ranked_data rd WHERE rd.Person = ranked_data.Person AND rd.Indicator = ranked_data.Indicator) ) ORDER BY Person, Indicator, row_num;
方法2:累计异常次数标记
通过累计异常次数,保留累计次数≤1的行(0表示未出现异常,1表示刚出现第一个异常):
WITH data_with_flag AS ( SELECT t.*, SUM(CASE WHEN Status = 'not OK' THEN 1 ELSE 0 END) OVER (PARTITION BY Person, Indicator ORDER BY row_num ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS error_count FROM your_table t ) SELECT Person, Indicator, Date, Prev_Date, Frequency, row_num, Status FROM data_with_flag WHERE error_count <= 1 ORDER BY Person, Indicator, row_num;
说明
- 方法1通过定位首个异常行号,直接筛选范围;
NVL处理无异常场景,取分组最大行号保留全部数据。 - 方法2通过累计计数标记,逻辑更直观,无需额外子查询处理无异常情况。
内容的提问来源于stack exchange,提问作者goldmariek
相关产品推荐
相关产品推荐

