当前位置: 首页 > news >正文

Oracle实战:相同批次下D5_D10最新数据整合为一行的3种方案

Oracle 实战:相同批次下 D5/D10 最新数据整合为一行的 3 种方案

在工业检测、数据统计等场景中,我们常遇到这样的需求:Oracle 表中存储了多个批次的检测数据,D5 和 D10 是单独的检测字段(非行数据),一个批次可能只测 D5、只测 D10,或两者都测;需要按批次分组,提取每个批次中 “已测试项目的最新数据”,并将 D5、D10 的最新值整合为一行显示(未测试项显示 NULL)。

本文将通过 “需求拆解→表结构定义→三种方案实现→方法对比” 的逻辑,带你彻底解决这个问题。
在这里插入图片描述

一、需求拆解与表结构定义

在写代码前,先明确核心要素,避免后续理解偏差。

1. 核心需求

  • batch_no(批次号)分组,每个批次输出一行结果;

  • 对 D5 字段:只从 “D5 非 NULL” 的记录中取最新更新时间对应的 D5 值;

  • 对 D10 字段:只从 “D10 非 NULL” 的记录中取最新更新时间对应的 D10 值;

  • 未测试的字段(如某批次只测了 D5),结果中显示 NULL,不报错。

2. 表结构与测试数据

假设表名为test_batch_data,存储检测批次数据,字段定义如下:

字段名数据类型说明
batch_noVARCHAR2(20)批次号(分组依据)
d5_valueNUMBER(10,2)D5 检测值(可 NULL)
d10_valueNUMBER(10,2)D10 检测值(可 NULL)
update_timeDATE数据更新时间(取最新用)
插入测试数据

为了模拟 “部分批次只测单字段” 的场景,插入 5 条测试数据:

-- 创建表(若未存在)
CREATE TABLE test_batch_data (batch_no    VARCHAR2(20) NOT NULL,d5_value    NUMBER(10,2),d10_value   NUMBER(10,2),update_time DATE NOT NULL
);-- 插入测试数据
INSERT INTO test_batch_data VALUES ('B2024060101', 12.30, NULL, TO_DATE('2024-06-01 10:00:00', 'YYYY-MM-DD HH24:MI:SS'));
INSERT INTO test_batch_data VALUES ('B2024060101', NULL, 23.50, TO_DATE('2024-06-01 10:05:00', 'YYYY-MM-DD HH24:MI:SS'));
INSERT INTO test_batch_data VALUES ('B2024060101', 12.50, NULL, TO_DATE('2024-06-02 09:30:00', 'YYYY-MM-DD HH24:MI:SS')); -- D5更新
INSERT INTO test_batch_data VALUES ('B2024060102', NULL, 21.10, TO_DATE('2024-06-01 14:00:00', 'YYYY-MM-DD HH24:MI:SS')); -- 只测D10
INSERT INTO test_batch_data VALUES ('B2024060103', 13.00, NULL, TO_DATE('2024-06-02 11:00:00', 'YYYY-MM-DD HH24:MI:SS')); -- 只测D5
COMMIT;

测试数据说明:

  • B2024060101:测了 D5(2 次更新)和 D10(1 次);

  • B2024060102:只测了 D10;

  • B2024060103:只测了 D5。

二、三种核心实现方案

根据 Oracle 版本兼容性和性能需求,提供三种方案,覆盖从 “简洁” 到 “高性能” 的不同场景。

方案 1:FIRST_VALUE 窗口函数(简洁版,Oracle 10g+)

核心思路

利用FIRST_VALUE()窗口函数,按批次分组后:

  • 对 D5:只在 “d5_value 非 NULL” 的记录中,按update_time倒序取第一条值(即最新值);

  • 对 D10:同理,只在 “d10_value 非 NULL” 的记录中取最新值;

  • 最后用MAX()+GROUP BY合并为一行(窗口函数会生成多行相同批次数据,需聚合去重)。

完整 SQL 代码
SELECT batch_no,MAX(d5_latest) AS d5_latest_value,  -- 聚合去重,保留批次唯一行MAX(d10_latest) AS d10_latest_value
FROM (SELECT batch_no,-- 取D5最新值:d5_value非NULL时按时间倒序,NULL值排最后FIRST_VALUE(d5_value) OVER(PARTITION BY batch_no ORDER BY CASE WHEN d5_value IS NOT NULL THEN update_time END DESC NULLS LAST) AS d5_latest,-- 取D10最新值:逻辑同上FIRST_VALUE(d10_value) OVER(PARTITION BY batch_no ORDER BY CASE WHEN d10_value IS NOT NULL THEN update_time END DESC NULLS LAST) AS d10_latestFROM test_batch_data
) t
GROUP BY batch_no
ORDER BY batch_no;
关键细节解释
  1. CASE WHEN d5_value IS NOT NULL THEN update_time
  • 仅当 D5 有值时,才用update_time排序;无值时返回 NULL,避免 “NULL 时间” 干扰排序。
  1. NULLS LAST
  • Oracle 中,排序时 NULL 默认排在最后,但显式声明可避免不同环境的排序差异,确保逻辑稳定。
  1. MAX()聚合:
  • 内层查询中,同一批次的所有行都会携带相同的d5_latestd10_latestMAX()可快速合并为一行。
执行结果
batch_nod5_latest_valued10_latest_value
B202406010112.5023.50
B2024060102NULL21.10
B202406010313.00NULL

方案 2:分查 JOIN(兼容版,Oracle 8i+)

核心思路

若需兼容 Oracle 10g 以下旧版本(不支持FIRST_VALUE),可拆分为三步:

  1. 单独查询 “每个批次的 D5 最新值”(过滤 d5_value 非 NULL,按时间倒序取第一条);

  2. 单独查询 “每个批次的 D10 最新值”(逻辑同上);

  3. FULL OUTER JOIN合并两个结果集,确保 “只测单字段的批次” 不丢失。

完整 SQL 代码
-- 最终合并结果
SELECT COALESCE(a.batch_no, b.batch_no) AS batch_no,  -- 处理某侧为NULL的批次号a.d5_latest_value,b.d10_latest_value
FROM (-- 子查询1:取每个批次的D5最新值SELECT batch_no,d5_value AS d5_latest_valueFROM (SELECT batch_no,d5_value,ROW_NUMBER() OVER(PARTITION BY batch_no ORDER BY update_time DESC) AS rn  -- 按时间倒序,最新为1FROM test_batch_dataWHERE d5_value IS NOT NULL  -- 只处理有D5值的记录) t1WHERE rn = 1  -- 保留最新一条
) a
FULL OUTER JOIN (-- 子查询2:取每个批次的D10最新值(逻辑同D5)SELECT batch_no,d10_value AS d10_latest_valueFROM (SELECT batch_no,d10_value,ROW_NUMBER() OVER(PARTITION BY batch_no ORDER BY update_time DESC) AS rnFROM test_batch_dataWHERE d10_value IS NOT NULL) t2WHERE rn = 1
) b ON a.batch_no = b.batch_no  -- 按批次号关联
ORDER BY batch_no;
关键细节解释
  1. FULL OUTER JOIN
  • 若用INNER JOIN,会丢失 “只测 D5” 或 “只测 D10” 的批次,FULL OUTER JOIN可完整保留所有批次。
  1. COALESCE(a.batch_no, b.batch_no)
  • 当某批次只在 A 表(D5)或 B 表(D10)中存在时,其中一侧的batch_no为 NULL,COALESCE可取出非 NULL 的批次号。
执行结果

与方案 1 完全一致,兼容旧版本是其核心优势。

方案 3:单次扫描高性能版(Oracle 10g+)

核心思路

方案 1 和 2 在表数据量大时,可能存在 “多次扫描表” 的问题(如方案 2 需扫描表 2 次)。本方案通过 “单次表扫描 + 条件聚合”,将 D5、D10 的最新值计算逻辑合并,减少 IO 开销,提升性能。

完整 SQL 代码
SELECT batch_no,-- 取D5最新值:按批次分组,筛选d5非NULL的记录,取时间最大的d5值MAX(CASE WHEN d5_value IS NOT NULL AND update_time = d5_max_time THEN d5_value END) AS d5_latest_value,-- 取D10最新值:逻辑同上MAX(CASE WHEN d10_value IS NOT NULL AND update_time = d10_max_time THEN d10_value END) AS d10_latest_value
FROM (-- 子查询:先获取每个批次的D5最大时间、D10最大时间SELECT batch_no,d5_value,d10_value,update_time,-- 每个批次中,D5非NULL记录的最大更新时间MAX(CASE WHEN d5_value IS NOT NULL THEN update_time END) OVER(PARTITION BY batch_no) AS d5_max_time,-- 每个批次中,D10非NULL记录的最大更新时间MAX(CASE WHEN d10_value IS NOT NULL THEN update_time END) OVER(PARTITION BY batch_no) AS d10_max_timeFROM test_batch_data
) t
GROUP BY batch_no
ORDER BY batch_no;
关键细节解释
  1. 内层子查询(单次扫描):
  • MAX() OVER(PARTITION BY batch_no)一次性计算出 “每个批次 D5 的最大时间” 和 “D10 的最大时间”,仅扫描表 1 次。
  1. 外层条件聚合:
  • 对 D5:当 “当前记录的 D5 非 NULL” 且 “更新时间等于该批次 D5 最大时间” 时,取该 D5 值,再用MAX()聚合(实际只有一条符合条件,聚合仅为去重)。
性能优势
  • 表扫描次数:仅 1 次(方案 2 需 2 次);

  • 窗口函数计算:内层仅 2 个MAX()窗口函数,计算开销低;

  • 适合场景:千万级以上数据量的大表,需优先保证查询性能。

三、方法对比与最佳实践

方案适用 Oracle 版本表扫描次数优点缺点推荐场景
方案 1(FIRST_VALUE)10g+1 次代码简洁,易维护依赖窗口函数,旧版不支持10g + 版本,数据量中等
方案 2(分查 JOIN)8i+2 次兼容所有旧版本扫描次数多,大表性能差旧版本(8i/9i),小表
方案 3(单次扫描)10g+1 次性能最优,大表友好逻辑稍复杂10g + 版本,千万级大表

最佳实践建议

  1. 优先用方案 3:若版本支持(10g+)且数据量较大,方案 3 的 “单次扫描” 能显著减少 IO,是性能最优解;

  2. 快速实现用方案 1:若数据量小(万级以内),方案 1 的代码更简洁,开发效率高;

  3. 旧版本用方案 2:仅当必须兼容 Oracle 9i 及以下时,才选择方案 2,且建议对batch_noupdate_time建立索引(提升子查询排序效率)。

四、总结

本文解决的核心问题是 “Oracle 中相同批次下,多字段(D5/D10)最新数据的整合”,其本质思路可归纳为:

  1. 按批次分组:以batch_no为分组依据,确保每个批次输出一行;

  2. 字段单独取最新:对每个检测字段(D5/D10),单独过滤 “非 NULL 记录”,再按update_time取最新值;

  3. 结果整合:通过聚合(如 MAX)或 JOIN,将多个字段的最新值合并为一行。

无论选择哪种方案,都需注意 “NULL 值处理”(如NULLS LASTCOALESCE)和 “性能优化”(如减少表扫描、索引建立)。若你的表中还有其他检测字段(如 D3、D7),只需按相同逻辑扩展字段处理即可。

http://www.dtcms.com/a/457113.html

相关文章:

  • 私人精品货源网站有哪些php网站开发前端
  • 金融 - 搭建 图谱挖掘工作流 调研
  • 图像分割关于DualSeg,FFM和CFM的论文学习
  • Spring的 `@Import`注解 笔记251008
  • 【玩泰山派】4、制作ubuntu镜像-(6)使用鲁班猫的sdk去制作镜像
  • 长兴县住房和城乡建设局网站我想看黄牌
  • 深入理解 Reactor 反应堆模式:高性能网络编程的核心
  • php做小公司网站用什么框架医药招商网站大全免费
  • 从 0 到 1 掌控云原生部署:Java 项目的 Docker 容器化与 K8s 集群实战指南
  • 哪里可以做足球网站虚拟主机 2个网站
  • 建设银行的英语网站首页dede导入wordpress
  • 支付宝小程序 MAU 增长新路径:生态工具与流量闭环的协同实战
  • C++ 成员初始化列表
  • 三门县住房和城乡建设规划局网站商业网站是怎么做的
  • Spring Security 最简配置完全指南-从入门到精通前后端分离安全配置
  • Go泛型实战指南:从入门到工程最佳实践|Go语言进阶(12)
  • easyexcel实现excel读取
  • 用jsp实现网站开发实例高校网站站群建设公司
  • 个人网站导航html源码团购网站模板
  • wpf之RelativeSource用法总结
  • 【C语言基础详细版】06. 动态内存管理:从原理到实战应用
  • 磁悬浮轴承转子不平衡质量控制深度解析
  • 关于力扣2025.10.8每日的收货
  • 烟台做网站的价格网络工程是冷门专业吗
  • 亲测可用,R语言 ggplot2 箱线图线条控制参数详解,箱线图离散数值控制
  • 沙漠风网站建设公司太原不错的互联网公司
  • 记录thinkphp模型查询时select与count执行顺序的疑问
  • AI编写的一个服务器监控源码
  • C# TCP 客户端开发笔记(TcpClient)
  • 网站建设数据库怎么弄个人养老金交15年领多少