前言 如果有一张表,我们既想对它更新,又想对它插入应该如何操作? 可以使用UPDATE和INSERT完成你的目标。 如果你的数据量很大,想尽快完成任务执行,可否有其他方案?那一定不要错过GaussDB(DWS)的MERGE INTO功能。 MERGE INTO 概念 MERGE INTO是SQL 2003引入的标准。 If a table T, as well as being updatable, is insertable-into, then rows can be inserted into it (subject to applicable Access Rules and Conformance Rules). The primary effect of an <insert statement> on T is to insert into T each of the zero or more rows contained in a specified table. The primary effect of a <merge statement> on T is to replace zero or more rows in T with specified rows and/or to insert into T zero or more specified rows, depending on the result of a <search condition> and on whether one or both of <merge when matched clause> and <merge when not matched clause> are specified. 一张表在一条语句里面既可以被更新,也可以被插入。是否被更新还是插入取决于search condition的结果和指定的merge when matched clause(当condition匹配时做什么操作)和merge when not matched clause(当condition不匹配时做什么操作)语法。 SQL 2008进行了扩展,可以使用多个MATCHED 和NOT MATCHED 。 MERGE has been extended to support multiple MATCHED and NOT MATCHED clauses, each accompanied by a search condition, that gives much greater flexibility in the coding of complex MERGE statements to handle update conflicts. MERGE INTO 命令涉及到两张表。目标表:被插入或者更新的表。源表:用于跟目标表进行匹配的表,目标表的数据来源。 MERGE INTO语句将目标表和源表中数据针对关联条件进行匹配,若关联条件匹配时对目标表进行UPDATE,无法匹配时对目标表执行INSERT。 使用场景:当业务中需要将一个表中大量数据添加到现有表时,使用MERGE INTO 可以高效地将数据导入,避免多次INSERT+UPDATE操作。 MERGE INTO 语法 GaussDB(DWS) MERGE INTO 语法如下: MERGE INTO table_name [ [ AS ] alias ] USING { { table_name | view_name } | subquery } [ [ AS ] alias ] ON ( condition ) [ WHEN MATCHED THEN UPDATE SET { column_name = { expression | DEFAULT } | ( column_name [, ...] ) = ( { expression | DEFAULT } [, ...] ) } [, ...] [ WHERE condition ] ] [ WHEN NOT MATCHED THEN INSERT { DEFAULT VALUES | [ ( column_name [, ...] ) ] VALUES ( { expression | DEFAULT } [, ...] ) [, ...] [ WHERE condition ] } ]; INTO 指定目标表。 USING 指定源表。源表可以是普通表,也可以是子查询。 ON 关联条件,用于指定目标表和源表的关联条件。 WHEN MATCHED 当源表和目标表中数据可以匹配关联条件时,选择WHEN MATCHED子句执行UPDATE操作。 WHEN NOT MATCHED 当源表和目标表中数据无法匹配关联条件时,选择WHEN NOT MATCHED子句执行INSERT操作。 WHEN MATCHED,WHEN NOT MATCHED 可以缺省一个,不能指定多个。 WHEN MATCHED,WHEN NOT MATCHED 可以使用WHERE进行条件过滤。 WHEN MATCHED,WHEN NOT MATCHED 顺序可以交换。 实战应用 首先创建好下面几张表,用于执行MREGE INTO 操作。 gaussdb=# CREATE TABLE dst ( product_id INT, product_name VARCHAR(20), category VARCHAR(20), total INT ) DISTRIBUTE BY HASH(product_id); gaussdb=# CREATE TABLE dst_data ( product_id INT, product_name VARCHAR(20), category VARCHAR(20), total INT ) DISTRIBUTE BY HASH(product_id); gaussdb=# CREATE TABLE src ( product_id INT, product_name VARCHAR(20), category VARCHAR(20), total INT ) DISTRIBUTE BY HASH(product_id); gaussdb=# INSERT INTO dst_data VALUES(1601,'lamaze','toys',100),(1600,'play gym','toys',100),(1502,'olympus','electrncs',100),(1501,'vivitar','electrnc',100),(1666,'harry potter','dvd',100); gaussdb=# INSERT INTO src VALUES(1700,'wait interface','books',200),(1666,'harry potter','toys',200),(1601,'lamaze','toys',200),(1502,'olympus camera','electrncs',200); gaussdb=# INSERT INTO dst SELECT * FROM dst_data; 同时指定WHEN MATCHED 与WHEN NOT MATCHED 查看计划,看下MERGE INTO是如何执行的。 MERGE INTO转化成JOIN将两个表进行关联处理,关联条件就是ON后指定的条件。 gaussdb=# EXPLAIN (COSTS off) MERGE INTO dst x USING src y ON x.product_id = y.product_id WHEN MATCHED THEN UPDATE SET product_name = y.product_name, category = y.category, total = y.total WHEN NOT MATCHED THEN INSERT VALUES (y.product_id, y.product_name, y.category, y.total); QUERY PLAN -------------------------------------------------- id | operation -----+-------------------------------------------- 1 | -> Streaming (type: GATHER) 2 | -> Merge on dst x 3 | -> Streaming(type: REDISTRIBUTE) 4 | -> Hash Left Join (5, 6) 5 | -> Seq Scan on src y 6 | -> Hash 7 | -> Seq Scan on dst x Predicate Information (identified by plan id) ------------------------------------------------ 4 --Hash Left Join (5, 6) Hash Cond: (y.product_id = x.product_id) (14 rows) 为什么这里转化成了LEFT JOIN? 由于需要在目标表与源表匹配时更新目标表,不匹配时向目标表插入数据。也就是源表的一部分数据用于更新目标表,另一部分用于向目标表插入。与LEFT JOIN语义是相似的。 5 --Seq Scan on public.src y Output: y.product_id, y.product_name, y.category, y.total, y.ctid Distribute Key: y.product_id 6 --Hash Output: x.product_id, x.product_name, x.category, x.total, x.ctid, x.xc_node_id 7 --Seq Scan on public.dst x Output: x.product_id, x.product_name, x.category, x.total, x.ctid, x.xc_node_id Distribute Key: x.product_id 执行MERGE INTO,查看结果。 两张表在product_id是1502,1601,1666时可以关联,所以这三条记录被更新。src表product_id是1700时未匹配,插入此条记录。其他未修改。 gaussdb=# SELECT * FROM dst ORDER BY 1; product_id | product_name | category | total ------------+--------------+-----------+------- 1501 | vivitar | electrnc | 100 1502 | olympus | electrncs | 100 1600 | play gym | toys | 100 1601 | lamaze | toys | 100 1666 | harry potter | dvd | 100 (5 rows) gaussdb=# SELECT * FROM src ORDER BY 1; product_id | product_name | category | total ------------+----------------+-----------+------- 1502 | olympus camera | electrncs | 200 1601 | lamaze | toys | 200 1666 | harry potter | toys | 200 1700 | wait interface | books | 200 (4 rows) gaussdb=# MERGE INTO dst x USING src y ON x.product_id = y.product_id WHEN MATCHED THEN UPDATE SET product_name = y.product_name, category = y.category, total = y.total WHEN NOT MATCHED THEN INSERT VALUES (y.product_id, y.product_name, y.category, y.total); MERGE 4 gaussdb=# SELECT * FROM dst ORDER BY 1; product_id | product_name | category | total ------------+----------------+-----------+------- 1501 | vivitar | electrnc | 100 -- 未修改 1502 | olympus camera | electrncs | 200 -- 更新 1600 | play gym | toys | 100 -- 未修改 1601 | lamaze | toys | 200 -- 更新 1666 | harry potter | toys | 200 -- 更新 1700 | wait interface | books | 200 -- 插入 (6 rows) 查看具体UPDATE、INSERT个数 可以通过EXPLAIN PERFORMANCE或者EXPLAIN ANALYZE查看UPDATE、INSERT各自个数。(这里仅显示必要部分) 在Predicate Information部分可以看到总共插入一条,更新三条。 在Datanode Information部分可以看到每个节点的信息。datanode1上更新2条,datanode2上插入一条,更新1条。 gaussdb=# EXPLAIN PERFORMANCE MERGE INTO dst x USING src y ON x.product_id = y.product_id WHEN MATCHED THEN UPDATE SET product_name = y.product_name, category = y.category, total = y.total WHEN NOT MATCHED THEN INSERT VALUES (y.product_id, y.product_name, y.category, y.total); Predicate Information (identified by plan id) ------------------------------------------------ 2 --Merge on public.dst x Merge Inserted: 1 Merge Updated: 3 Datanode Information (identified by plan id) --------------------------------------------------------------------------------------- 2 --Merge on public.dst x datanode1 (Tuple Inserted 0, Tuple Updated 2) datanode2 (Tuple Inserted 1, Tuple Updated 1) 省略WHEN NOT MATCHED 部分。 这里由于没有WHEN NOT MATCHED部分,在两个表不匹配时不需要执行任何操作,也就不需要源表这部分的数据,所有只需要inner join即可。 gaussdb=# EXPLAIN (COSTS off) MERGE INTO dst x USING src y ON x.product_id = y.product_id WHEN MATCHED THEN UPDATE SET product_name = y.product_name, category = y.category, total = y.total; QUERY PLAN -------------------------------------------------- id | operation ----+----------------------------------- 1 | -> Streaming (type: GATHER) 2 | -> Merge on dst x 3 | -> Hash Join (4,5) 4 | -> Seq Scan on dst x 5 | -> Hash 6 | -> Seq Scan on src y Predicate Information (identified by plan id) ------------------------------------------------ 3 --Hash Join (4,5) Hash Cond: (x.product_id = y.product_id) (13 rows) 执行后查看结果。MERGE INTO只操作了3条数据。 gaussdb=# truncate dst; gaussdb=# INSERT INTO dst SELECT * FROM dst_data; gaussdb=# MERGE INTO dst x USING src y ON x.product_id = y.product_id WHEN MATCHED THEN UPDATE SET product_name = y.product_name, category = y.category, total = y.total; MERGE 3 gaussdb=# SELECT * FROM dst; product_id | product_name | category | total ------------+----------------+-----------+------- 1501 | vivitar | electrnc | 100 -- 未修改 1502 | olympus camera | electrncs | 200 -- 更新 1600 | play gym | toys | 100 -- 未修改 1601 | lamaze | toys | 200 -- 更新 1666 | harry potter | toys | 200 -- 更新 (5 rows) 省略WHEN NOT MATCHED 只有在不匹配时进行插入。结果中没有数据被更新。 gaussdb=# EXPLAIN (COSTS off) MERGE INTO dst x USING src y ON x.product_id = y.product_id WHEN NOT MATCHED THEN INSERT VALUES (y.product_id, y.product_name, y.category, y.total); QUERY PLAN -------------------------------------------------- id | operation ----+----------------------------------------- 1 | -> Streaming (type: GATHER) 2 | -> Merge on dst x 3 | -> Streaming(type: REDISTRIBUTE) 4 | -> Hash Left Join (5, 6) 5 | -> Seq Scan on src y 6 | -> Hash 7 | -> Seq Scan on dst x Predicate Information (identified by plan id) ------------------------------------------------ 4 --Hash Left Join (5, 6) Hash Cond: (y.product_id = x.product_id) (14 rows) gaussdb=# truncate dst; gaussdb=# INSERT INTO dst SELECT * FROM dst_data; gaussdb=# MERGE INTO dst x USING src y ON x.product_id = y.product_id WHEN NOT MATCHED THEN INSERT VALUES (y.product_id, y.product_name, y.category, y.total); MERGE 1 gaussdb=# SELECT * FROM dst ORDER BY 1; product_id | product_name | category | total ------------+----------------+-----------+------- 1501 | vivitar | electrnc | 100 -- 未修改 1502 | olympus | electrncs | 100 -- 未修改 1600 | play gym | toys | 100 -- 未修改 1601 | lamaze | toys | 100 -- 未修改 1666 | harry potter | dvd | 100 -- 未修改 1700 | wait interface | books | 200 -- 插入 (6 rows) WHERE过滤条件 语义是在进行更新或者插入前判断当前行是否满足过滤条件,如果不满足,就不进行更新或者插入。如果对于字段不想被更新,需要指定过滤条件。 下面例子在两表可关联时,只会更新product_name = 'olympus’的行。在两表无法关联时且源表的product_id != 1700时才会进行插入。 gaussdb=# truncate dst; gaussdb=# INSERT INTO dst SELECT * FROM dst_data; gaussdb=# MERGE INTO dst x USING src y ON x.product_id = y.product_id WHEN MATCHED THEN UPDATE SET product_name = y.product_name, category = y.category, total = y.total WHERE x.product_name = 'olympus' WHEN NOT MATCHED THEN INSERT VALUES (y.product_id, y.product_name, y.category, y.total) WHERE y.product_id != 1700; MERGE 1 gaussdb=# SELECT * FROM dst ORDER BY 1; SELECT * FROM dst ORDER BY 1; product_id | product_name | category | total ------------+----------------+-----------+------- 1501 | vivitar | electrnc | 100 1502 | olympus camera | electrncs | 200 1600 | play gym | toys | 100 1601 | lamaze | toys | 100 1666 | harry potter | dvd | 100 (5 rows) 子查询 在USING部分可以使用子查询,进行更复杂的关联操作。 对源表进行聚合操作的结果再与目标表匹配 MERGE INTO dst x USING ( SELECT product_id, product_name, category, sum(total) AS total FROM src group by product_id, product_name, category ) y ON x.product_id = y.product_id WHEN MATCHED THEN UPDATE SET product_name = x.product_name, category = x.category, total = x.total WHEN NOT MATCHED THEN INSERT VALUES (y.product_id, y.product_name, y.category, y.total + 200); 多个表UNION后的结果再与目标表匹配 MERGE INTO dst x USING ( SELECT 1501 AS product_id, 'vivitar 35mm' AS product_name, 'electrncs' AS category, 100 AS total UNION ALL SELECT 1666 AS product_id, 'harry potter' AS product_name, 'dvd' AS category, 100 AS total ) y ON x.product_id = y.product_id WHEN MATCHED THEN UPDATE SET product_name = x.product_name, category = x.category, total = x.total WHEN NOT MATCHED THEN INSERT VALUES (y.product_id, y.product_name, y.category, y.total + 200); 存储过程 gaussdb=# CREATE OR REPLACE PROCEDURE store_procedure1() AS BEGIN MERGE INTO dst x USING src y ON x.product_id = y.product_id WHEN MATCHED THEN UPDATE SET product_name = y.product_name, category = y.category, total = y.total; END; / CREATE PROCEDURE gaussdb=# CALL store_procedure1(); MERGE INTO背后原理 上文提到了MREGE INTO转化成LEFT JOIN或者INNER JOIN将目标表和源表进行关联。那么如何知道某一行要进行更新还是插入? 通过EXPLAIN VERBOSE查看算子的输出。扫描两张表时都输出了ctid列。那么ctid列有什么作用呢? 5 --Seq Scan on public.src y Output: y.product_id, y.product_name, y.category, y.total, y.ctid Distribute Key: y.product_id 6 --Hash Output: x.product_id, x.product_name, x.category, x.total, x.ctid, x.xc_node_id 7 --Seq Scan on public.dst x Output: x.product_id, x.product_name, x.category, x.total, x.ctid, x.xc_node_id Distribute Key: x.product_id ctid标识了这一行在存储上具体位置,知道了这个位置就可以对这个位置的数据进行更新。GaussDB(DWS)作为MPP分布式数据库,还需要知道节点的信息(xc_node_id)。UPDATE操作需要这两个值。 在MREGE INTO这里ctid还另有妙用。当目标表匹配时需要更新,这是就保留本行ctid值。如果无法匹配,插入即可。就不需要ctid,此时可认识ctid值是NULL。根据LEFT JOIN输出的ctid结果是否为NULL,最终决定本行该被更新还是插入。 这样在两张表做完JOIN操作后,根据JOIN后输出的ctid列,更新或者插入某一行。 注意事项 使用MERGE INTO时要注意匹配条件是否合适。如果不注意,容易造成数据被非预期更新,可能整张表被更新。 总结 GAUSSDB(DWS)提供了高效的数据导入的功能MERGE INTO,对于数据仓库是一项非常关键的功能。可以使用MERGE INTO 同时更新和插入一张表,在数据量非常大的情况下也能很快完成地数据导入。
一招教你数据仓库如何高效批量导入与更新数据
来源:这里教程网
时间:2026-03-03 16:50:50
作者:
编辑推荐:
- 一招教你数据仓库如何高效批量导入与更新数据03-03
- 【Datapump】Oracle数据泵迁移数据命令参考(expdp/impdp说明)03-03
- 致同首席合伙人李惠琦:品牌是最重要的无形资产03-03
- 一夜爆火的鸿星尔克还差点什么?03-03
- ORA-00445: background process "m000" did not start after 120 seconds host:xxxx03-03
- Oracle:19c 新特性——Memoptimized Rowstore 简介03-03
- Cell smart table scan等待事件03-03
- 【SQL】Oracle 表添加列提高效率语句参考03-03
下一篇:
相关推荐
-
雷神推出 MIX PRO II 迷你主机:基于 Ultra 200H,玻璃上盖 + ARGB 灯效
2 月 9 日消息,雷神 (THUNDEROBOT) 现已宣布推出基于英
-
制造商 Musnap 推出彩色墨水屏电纸书 Ocean C:支持手写笔、第三方安卓应用
2 月 10 日消息,制造商 Musnap 现已在海外推出一款 Oce
热文推荐
- 一夜爆火的鸿星尔克还差点什么?
一夜爆火的鸿星尔克还差点什么?
26-03-03 - Oracle:19c 新特性——Memoptimized Rowstore 简介
- 西瓜视频做那个领域比较容易实现变现?
西瓜视频做那个领域比较容易实现变现?
26-03-03 - ORA-01245 ORA-01110故障恢复
ORA-01245 ORA-01110故障恢复
26-03-03 - 一键解决ORA-00279 ORA-00289 ORA-00280
一键解决ORA-00279 ORA-00289 ORA-00280
26-03-03 - Oracle Recovery Tools恢复MISSING00000文件故障
- Oracle:容器数据库简介
Oracle:容器数据库简介
26-03-03 - 修改数据库字符集导致的数据异常
修改数据库字符集导致的数据异常
26-03-03 - 一键恢复ORA-01113 ORA-01110—Oracle Recovery Tools
- “你荐书,我买单!”——快来抱走你的精神食粮
“你荐书,我买单!”——快来抱走你的精神食粮
26-03-03
