从匹配案例的另一个表值更新 Oracle 表

Posted

技术标签:

【中文标题】从匹配案例的另一个表值更新 Oracle 表【英文标题】:Update Oracle table from another table value on matching case 【发布时间】:2020-04-28 00:18:28 【问题描述】:

我有一个主表(比如 tableA,其中包含 tab_a_id、field_code、field_id 列)。还有另一个表,比如 tableB,它有列 area_id 和 area_code。 tab_a_id 是 TableA 的主键。我想根据 field_code 更新 tableA 的 field_id。 tableA 的 field_code 和 tableB 的 area_code 匹配但不相同,意味着 field_code 有其他与 area_code 列不匹配的值。我想设置 field_id = area_id if field_code = area_code 但是,如果不匹配,它应该设置为默认值 -1,即“未知”字段。我尝试使用子查询和批量更新(例如 Update tableA SET field_id = (SELECT area_code from tableB where area_code = field_code))。这适用于有限的数据集。但我有 300 万条匹配记录,这意味着 300 万条子查询。另一个问题是有 700 万条记录,导致 400 万条不匹配的记录和无用的子查询。 是否有任何最佳方法可以以最少的时间和更高的效率更新此类记录。我尝试了合并命令,但与 forall 循环查询相比,它的性能很差

【问题讨论】:

我听不懂这个问题。样本数据,期望的结果会有所帮助。 请在代码问题中给出minimal reproducible example--剪切&粘贴&运行代码;具有期望和实际输出(包括逐字错误消息)的示例输入(作为初始化代码);标签和版本;明确的规范和解释。对于包含 DBMS 和 DDL 的 SQL,其中包括约束、索引和表格初始化。对于包括 EXPLAIN 结果和统计信息的 SQL 性能。 (约束、索引和计划至关重要。)在考虑发布之前请先研究一下。这包括 SQL 优化/性能的基础——直接导致索引、计划、统计和 SARGability。 How to Ask 【参考方案1】:

更新 700 万行中的 3 行似乎是这里的问题。

我在小型机器上的数据库中创建了一个测试集,获得结果的最快方法是创建一个包含所需数据和稍后交换名称的新表 (CTAS)。我没有使用主键列 tab_a_id 来简化答案。

CREATE TABLE a (field_id NUMBER, field_code VARCHAR2(30)) NOLOGGING;

CREATE TABLE b (area_id NUMBER, area_code VARCHAR2(30)) NOLOGGING;

使用MERGEUPDATE 相当慢(15 分钟),大概是因为改动量大:

UPDATE a SET field_id=-1 WHERE field_code NOT IN (SELECT area_code FROM b);
5,599,989 rows updated. (560 seconds)

MERGE INTO a USING b ON (a.field_code=b.area_code)
 WHEN MATCHED THEN UPDATE SET a.field_id = b.area_id;
2,400,011 rows merged. (232 seconds)

但是,使用更改的数据创建新表的速度提高了 20 倍,并且只需要 38 秒:

CREATE TABLE x NOLOGGING AS 
SELECT a.field_id, NVL(b.area_code, -1) AS field_code
  FROM a JOIN b ON a.field_code=b.area_code;

这里是测试数据生成:

INSERT /*+ APPEND */ INTO a (field_id, field_code) SELECT id, to_char(id) from (select level as id from dual connect by rownum <= 1000000); COMMIT;
INSERT /*+ APPEND */ INTO a (field_id, field_code) SELECT field_id+1000000, to_char(field_id+1000000) from a; COMMIT;
INSERT /*+ APPEND */ INTO a (field_id, field_code) SELECT field_id+2000000, to_char(field_id+2000000) from a; COMMIT;
INSERT /*+ APPEND */ INTO a (field_id, field_code) SELECT field_id+4000000, to_char(field_id+4000000) from a; COMMIT;
EXEC dbms_stats.gather_table_stats(null, 'a');

INSERT /*+ APPEND */ INTO b (area_id, area_code) SELECT -field_id, field_code FROM a SAMPLE (30);
exec dbms_stats.gather_table_stats(null, 'b');

【讨论】:

以上是关于从匹配案例的另一个表值更新 Oracle 表的主要内容,如果未能解决你的问题,请参考以下文章

内联表值 UDF 与视图 - 使用哪个?

使用案例表达式从另一个表更新表

用于从 N 记录的其他表值更新值的 SQL 查询

更新来自其他表的表值,如分布

Oracle SQL:使用中间第三个表引用的另一个表更新表列

内连接与外连接-及其典型案例