Oracle中基于列存在性与分组的两表取值方法问询
Oracle 19c两表索引匹配取值覆盖的高效实现
需求说明
基于tbl_a和tbl_b的ndx1、ndx2列匹配与存在性,将tbl_b作为tbl_a的覆盖表(当ndx1、ndx2匹配且tbl_b.val非空时生效),需满足指定取值规则,寻求Oracle 19c下的高效实现方法。
表结构与测试数据
CREATE TABLE tbl_a (id number, ndx1 number, ndx2 number, val number); CREATE TABLE tbl_b (id number, ndx1 number, ndx2 number, val number); INSERT INTO tbl_a VALUES (100, 1, 1, 2000); INSERT INTO tbl_a VALUES (200, 1, 1, 4000); INSERT INTO tbl_a VALUES (300, 1, 1, 6000); INSERT INTO tbl_a VALUES (400, 1, 1, 8000); INSERT INTO tbl_b VALUES (100, 1, 1, null); INSERT INTO tbl_b VALUES (200, 1, 1, 10000); INSERT INTO tbl_b VALUES (300, 1, 2, 40000);
tbl_a 查询结果
| ID | NDX1 | NDX2 | VAL |
|---|---|---|---|
| 100 | 1 | 1 | 2000 |
| 200 | 1 | 1 | 4000 |
| 300 | 1 | 1 | 6000 |
| 400 | 1 | 1 | 8000 |
tbl_b 查询结果
| ID | NDX1 | NDX2 | VAL |
|---|---|---|---|
| 100 | 1 | 1 | null |
| 200 | 1 | 1 | 10000 |
| 300 | 1 | 2 | 40000 |
取值规则
- 若两表
ndx1、ndx2均匹配,优先取b.val,若b.val为空则取a.val(即NVL(b.val, a.val)) - 若两表
ndx1匹配,但tbl_b无对应ndx2,取a.val - 若两表
ndx1匹配,但tbl_a无对应ndx2,取b.val - 不考虑
tbl_b中ndx1在tbl_a不存在的情况(以tbl_a为驱动表)
预期结果
| ID | NDX1 | NDX2 | VAL |
|---|---|---|---|
| 100 | 1 | 1 | 2000 |
| 200 | 1 | 1 | 10000 |
| 300 | 1 | 1 | 6000 |
| 300 | 1 | 2 | 40000 |
| 400 | 1 | 1 | 8000 |
高效实现方案
SQL 语句
-- 处理规则1、2:覆盖tbl_a所有记录,匹配到tbl_b同维度记录则取值覆盖 SELECT a.id, a.ndx1, a.ndx2, NVL(b.val, a.val) AS val FROM tbl_a a LEFT JOIN tbl_b b ON a.id = b.id AND a.ndx1 = b.ndx1 AND a.ndx2 = b.ndx2 UNION ALL -- 处理规则3:新增tbl_b中符合条件的独立记录 SELECT b.id, b.ndx1, b.ndx2, b.val AS val FROM tbl_b b WHERE EXISTS ( SELECT 1 FROM tbl_a a WHERE a.ndx1 = b.ndx1 ) AND NOT EXISTS ( SELECT 1 FROM tbl_a a WHERE a.id = b.id AND a.ndx1 = b.ndx1 AND a.ndx2 = b.ndx2 ) ORDER BY id, ndx2;
方案优势
- 高效性:使用
LEFT JOIN和EXISTS谓词,Oracle可利用索引快速定位匹配记录(建议为tbl_a创建(ndx1, id, ndx2)联合索引,为tbl_b创建(id, ndx1, ndx2)、(ndx1)索引),避免全表扫描。 - 逻辑清晰:通过
UNION ALL拆分两部分逻辑,分别处理覆盖和新增场景,无需去重操作,性能优于UNION。 - 符合规则:严格遵循所有取值规则,同时排除
tbl_b中ndx1不在tbl_a的记录。
内容的提问来源于stack exchange,提问作者DBox
相关产品推荐
相关产品推荐

