Oracle数据库中 merge into的用法

本文详细介绍了Oracle数据库中的MERGE命令,演示了如何使用MERGE命令在一个SQL语句中同时执行插入和更新操作,包括如何处理数据存在时的更新与不存在时的插入,以及如何将一个表的数据导入另一个表。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Oracle9g引入了MERGE命令,你能够在一个SQL语句中对一个表同时执行inserts和update操作. MERGE命令从一个或多个数据源中选择行来update或insert到一个或多个表.在Oracle 10g中MERGE有如下一些改进:

1、UPDATE或INSERT子句是可选的

2、UPDATE和INSERT子句可以加WHERE子句

3、在ON条件中使用常量过滤谓词来insert所有的行到目标表中,不需要连接源表和目标表

4、UPDATE子句后面可以跟DELETE子句来去除一些不需要的行

5、源表就是using关键字后面跟的表,目标表就是将要被merge into的表

6、merge into 中所有的update、insert、delete都是针对目标表来操作的。由于merge into已经制定了操作的表,所以update、insert、delete都不需要再显示指出表名

7、总之,merge into的作用就是 解决用B表跟新A表数据,如果A表中没有,则把B表的数据插入A表或向一个表中插入数据,如果该表已有该数据则更新,反之新增数据。
语法:

MERGE INTO [your table-name] [rename your table here]  
  
USING ( [write your query here] )[rename your query-sql and using just like a table]  
  
ON ([conditional expression here] AND [...]...)  
  
WHEN MATHED THEN [here you can execute some update sql or something else ]  
  
WHEN NOT MATHED THEN [execute something else here ! ]  

接下来我们来直接进行测试:

需求一:向一个表中插入一条数据,如果该表中已经有该数据则更新,反之新增

首先创建一个表TEST_ONE

create table TEST_ONE
(
  ID   NUMBER not null
    primary key,
  NAME VARCHAR2(255),
  IP   VARCHAR2(255),
  MEMO VARCHAR2(255)
)
commit;

随便添加几条数据作为测试数据

INSERT INTO TEST_ONE (ID, NAME, IP, MEMO) VALUES (1, '2', '3', '周文军');
INSERT INTO TEST_ONE (ID, NAME, IP, MEMO) VALUES (2, '66', '366', '2656');
INSERT INTO TEST_ONE (ID, NAME, IP, MEMO) VALUES (3, '5656', '626', '2626');
INSERT INTO TEST_ONE (ID, NAME, IP, MEMO) VALUES (4, '5656', '2626', '626');

好了,我们的数据表已经建成,如下图:
在这里插入图片描述
如果我们需要新增一条数据,一般会这样进行

INSERT INTO TEST_ONE (ID, NAME, IP, MEMO) VALUES (5, 'mrhu', '127.0.0.1.0', '王先生的IP');

但我们希望可以先用ID进行判断,没有该数据新增,有该数据更新,怎么实现呢?

那么merge into命令来了,直接撸代码:

merge into TEST_ONE mtb using (select '5' as id, 'mrhu' as name,'127.0.0.1.0' as ip,'王先生的IP' as memo from dual)mmb on (mtb.id = mmb.id)
when matched THEN
update set mtb.name = mmb.name,mtb.ip = mmb.ip,mtb.memo=mmb.memo
when not matched then
insert (mtb.id, mtb.name,mtb.ip,mtb.memo) VALUES(mmb.id,mmb.name,mmb.ip,mmb.memo);

运行如下:
在这里插入图片描述
我们再来看看表中数据:
在这里插入图片描述
数据新增成功!

那么我们如何测试更新呢?很简单,我们将memo=‘王先生的IP’ 改为 memo=‘本大美女的IP’来进行测试

merge into TEST_ONE mtb using (select '5' as id, 'mrhu' as name,'127.0.0.1.0' as ip,'本大美女的IP' as memo from dual)mmb on (mtb.id = mmb.id)
when matched THEN
update set mtb.name = mmb.name,mtb.ip = mmb.ip,mtb.memo=mmb.memo
when not matched then
insert (mtb.id, mtb.name,mtb.ip,mtb.memo) VALUES(mmb.id,mmb.name,mmb.ip,mmb.memo);

运行如下:
在这里插入图片描述
我们再来看看表中数据:
在这里插入图片描述
数据更新成功啦!

需求二:将A表中的数据添加到B表中,要求通过主键来进行判断,如果包含该数据则更新,反之新增

我们再创建一个表TEST_TWO作为表B,TEST_ONE作为表A

create table tes
(
  ID   NUMBER not null
    primary key,
  CODE VARCHAR2(255),
  MEMO VARCHAR2(255)
);
commit;

好了 表TEST_TWO建立好了,我们先来添加一条数据吧!

INSERT INTO ROOT.TEST_TWO (ID, CODE, MEMO) VALUES (5, 'mrhu', '隔壁老王的IP');

我们再看看TEST_TWO中的数据:
在这里插入图片描述
我们来将TEST_ONE中的数据导到我们新建的表中,通过分析,我们发现,TEST_TWO 表中有了一条数据,ID为5,TEST_ONE中也有一条ID为5的数据,预期执行效果为TEST_TWOID为5的数据的memo字段值将更新为TEST_ONE中的‘本大美女的IP’,其他值进行新增操作。

我们写代码来验证一下:

merge into TEST_TWO mtb using (select id,name,ip,memo from TEST_ONE) mmb on (mtb.id = mmb.id)
when matched THEN
UPDATE set mtb.code = mmb.name,mtb.memo = mmb.memo
when not matched THEN
insert (mtb.id,mtb.code,mtb.memo) values (mmb.id,mmb.name,mmb.memo);

我们来看看效果:
在这里插入图片描述
执行结果与预期结果一致,好了,merge into的用法你们学会了吗! 喜欢的关注支持一下!

<think>我们正在处理的是将数据从PostgreSQL迁移到Oracle数据库,并使用OracleMERGE INTO语句来实现插入或更新操作。由于源数据库是PostgreSQL,目标数据库Oracle,我们需要先通过某种方式将PostgreSQL的数据查询出来,然后作为源数据提供给OracleMERGE INTO语句。 但是,MERGE INTO语句的源数据必须是在Oracle数据库中可访问的。因此,我们需要将PostgreSQL的数据先导入到Oracle中(例如通过外部表、数据库链接、或中间文件等),或者在迁移过程中使用跨数据库连接(如dblink)直接访问PostgreSQL的数据(但这通常需要配置数据库网关,比较复杂)。 考虑到实际迁移场景,更常见的是先将PostgreSQL的数据导出为文件(如CSV),然后使用Oracle的外部表或SQL*Loader将数据加载到Oracle的临时表中,再使用MERGE INTO语句从临时表合并到目标表。 下面我们给出一个示例,假设我们已经将PostgreSQL的数据导入到Oracle数据库中的一个临时表(或中间表)PG_DATA_TEMP中,然后使用这个表作为源进行合并。 步骤: 1. 在Oracle中创建一个临时表(或普通表)用于存放从PostgreSQL导入的数据,假设表名为PG_DATA_TEMP。 2. 使用OracleMERGE INTO语句,将PG_DATA_TEMP表的数据合并到目标表(假设为TARGET_TABLE)。 示例MERGE INTO语句: ```sql MERGE INTO TARGET_TABLE T -- 目标表 USING ( SELECT ID, NAME, AMOUNT, REGION FROM PG_DATA_TEMP ) P -- 源数据(从PostgreSQL导入到Oracle的数据) ON (T.ID = P.ID) -- 匹配条件 WHEN MATCHED THEN UPDATE SET T.NAME = P.NAME, T.AMOUNT = P.AMOUNT, T.LAST_UPDATE = SYSDATE WHERE T.REGION != P.REGION -- 可选:只有区域不同时才更新 WHEN NOT MATCHED THEN INSERT (ID, NAME, AMOUNT, REGION, CREATE_DATE) VALUES (P.ID, P.NAME, P.AMOUNT, P.REGION, SYSDATE); ``` 但是,如果数据量很大,我们可能不希望创建中间表,而是希望直接通过数据库链接(dblink)从PostgreSQL读取数据(如果配置了Oracle到PostgreSQL的透明网关)。但这种方式配置复杂,且性能可能受网络影响。这里假设已经创建了一个数据库链接PG_LINK,则可以使用如下方式: ```sql MERGE INTO TARGET_TABLE T USING ( SELECT ID, NAME, AMOUNT, REGION FROM "PG_TABLE"@PG_LINK -- 通过dblink访问PostgreSQL的表 ) P ON (T.ID = P.ID) WHEN MATCHED THEN UPDATE SET T.NAME = P.NAME, T.AMOUNT = P.AMOUNT, T.LAST_UPDATE = SYSDATE WHERE T.REGION != P.REGION WHEN NOT MATCHED THEN INSERT (ID, NAME, AMOUNT, REGION, CREATE_DATE) VALUES (P.ID, P.NAME, P.AMOUNT, P.REGION, SYSDATE); ``` 注意:使用dblink时,表名和列名在PostgreSQL中是大小写敏感的,通常需要使用双引号括起来(如果创建时是大小写混合的)。在PostgreSQL中,如果表名是小写的,在Oracle中可以直接用小写,但建议用双引号。 然而,在实际迁移中,我们可能更倾向于使用ETL工具或者中间文件的方式,因为直接跨数据库操作可能不稳定。 另外,考虑到引用[1]中提到的国产化浪潮,很多企业可能正在从Oracle迁移到PostgreSQL,但这里用户的需求是反向的(从PG到Oracle),所以我们需要根据实际情况调整。 由于用户要求的是使用MERGE INTO语句的示例,我们重点展示了Oracle端的操作。至于如何将PostgreSQL的数据导入到Oracle,有多种方法,这里不展开。 下面我们针对MERGE INTO语句的一些要点进行说明: 1. **匹配条件**:ON子句中的条件应该使用唯一标识记录的列(如主键)。 2. **更新条件**:在WHEN MATCHED子句中,可以添加额外的条件(如上面例子中的WHERE子句)来限制更新的范围,避免不必要的更新。 3. **插入条件**:WHEN NOT MATCHED子句用于插入目标表中不存在的记录。 4. **性能考虑**:在目标表上,匹配条件的列(如ID)应该有索引,否则每次合并都会全表扫描。 如果迁移过程中数据量非常大,可以考虑分批提交,以避免大事务带来的性能问题和回滚段压力。 分批提交示例(使用循环,每次处理一定数量的行): ```sql DECLARE CURSOR c_data IS SELECT ID, NAME, AMOUNT, REGION FROM PG_DATA_TEMP ORDER BY ID; -- 按ID排序,便于分批 TYPE t_data IS TABLE OF c_data%ROWTYPE; v_data t_data; BEGIN OPEN c_data; LOOP FETCH c_data BULK COLLECT INTO v_data LIMIT 1000; -- 每次取1000行 EXIT WHEN v_data.COUNT = 0; FORALL i IN 1..v_data.COUNT MERGE INTO TARGET_TABLE T USING (SELECT v_data(i).ID AS ID, v_data(i).NAME AS NAME, v_data(i).AMOUNT AS AMOUNT, v_data(i).REGION AS REGION FROM DUAL) P ON (T.ID = P.ID) WHEN MATCHED THEN UPDATE SET T.NAME = P.NAME, T.AMOUNT = P.AMOUNT, T.LAST_UPDATE = SYSDATE WHERE T.REGION != P.REGION WHEN NOT MATCHED THEN INSERT (ID, NAME, AMOUNT, REGION, CREATE_DATE) VALUES (P.ID, P.NAME, P.AMOUNT, P.REGION, SYSDATE); COMMIT; -- 每1000行提交一次 END LOOP; CLOSE c_data; END; / ``` 注意:上面的分批处理使用了FORALL和MERGE,但MERGE在FORALL中每次只能处理一行(因为USING子句是单行)。所以这种方法效率不高。另一种做法是使用集合操作,构建一个包含所有批量数据的临时表(全局临时表),然后对这个临时表执行一次MERGE。或者,我们可以使用批量绑定结合动态SQL(使用EXECUTE IMMEDIATE)来实现,但这样写起来复杂。 更高效的做法是直接使用MERGE操作整个中间表(PG_DATA_TEMP),并确保有合适的索引,然后一次性提交。如果数据量极大,可以按范围分批(比如按ID分段),每次处理一段。 例如,按ID范围分批: ```sql BEGIN FOR i IN (SELECT MIN(id) min_id, MAX(id) max_id FROM PG_DATA_TEMP) LOOP FOR j IN i.min_id..i.max_id BY 1000000 -- 每次处理100万 LOOP MERGE INTO TARGET_TABLE T USING ( SELECT * FROM PG_DATA_TEMP WHERE id BETWEEN j AND j+999999 -- 每次取100万条 ) P ON (T.ID = P.ID) ...; -- 同上的更新和插入操作 COMMIT; END LOOP; END LOOP; END; / ``` 注意:这种范围分批要求ID是连续的,否则可能遗漏数据。如果不连续,可以使用ROWID分段或使用其他连续字段。 最后,考虑到引用[3]中提到的版本兼容性和语法严格性,请确保在目标Oracle数据库版本上测试MERGE语句。 根据引用[4]和[5],KingbaseES(一种国产数据库)对Oracle的兼容性较好,但这里我们是从PostgreSQL迁移到Oracle,所以不涉及KingbaseES。 综上所述,我们提供了从PostgreSQL迁移数据到Oracle时使用MERGE INTO的几种示例,包括使用中间表和分批次处理的方法。 相关问题: 1. 如何将PostgreSQL的数据高效导入到Oracle数据库? 2. 在Oracle中使用MERGE INTO时,如何优化大批量数据的性能? 3. 如何配置Oracle数据库链接(dblink)连接到PostgreSQL?</think>以下是将数据从 PostgreSQL 迁移到 Oracle 数据库时使用 `MERGE INTO` 语句的完整示例,结合了 Oracle 和 PostgreSQL 的最佳实践: ### 迁移场景说明 - **源数据库**:PostgreSQL (版本 12+) - **目标数据库****: Oracle (版本 19c+) - **需求**:将 PostgreSQL 的客户表数据合并到 Oracle 目标表 - 存在相同 `customer_id` 时更新记录 - 不存在时插入新记录 - 仅迁移最近 30 天更新的数据 ### 完整迁移步骤 #### 步骤 1: PostgreSQL 端数据准备 (使用 `dblink` 扩展) ```sql -- 启用 dblink 扩展 CREATE EXTENSION IF NOT EXISTS dblink; -- 创建 Oracle 连接配置 SELECT dblink_connect( 'oracle_conn', 'dbname=orcl user=admin password=**** host=oracle_host port=1521' ); ``` #### 步骤 2: Oracle 端目标表结构 ```sql CREATE TABLE customers_hist ( customer_id NUMBER(10) PRIMARY KEY, customer_name VARCHAR2(50), deposit NUMBER(15,2), amount NUMBER(15,2), region VARCHAR2(20), create_date DATE, last_update DATE ); ``` #### 步骤 3: 使用 MERGE INTO 迁移数据 (Oracle 端执行) ```sql MERGE INTO customers_hist T -- Oracle 目标表 USING ( SELECT * FROM TABLE( dblink('oracle_conn', $PG$ SELECT customer_id::text, -- 显式类型转换 customer_name, deposit, amount, region, create_date, last_update FROM pg_customers WHERE last_update > CURRENT_DATE - INTERVAL '30 days' $PG$ ) AS pg_data ( customer_id VARCHAR, customer_name VARCHAR, deposit NUMERIC, amount NUMERIC, region VARCHAR, create_date DATE, last_update DATE ) ) S -- PostgreSQL 源数据 ON (T.customer_id = TO_NUMBER(S.customer_id)) -- 类型转换匹配 WHEN MATCHED THEN UPDATE SET T.customer_name = S.customer_name, T.deposit = S.deposit, T.amount = S.amount, T.region = S.region, T.last_update = SYSDATE WHERE S.region = 'EAST' -- 条件更新 WHEN NOT MATCHED THEN INSERT (customer_id, customer_name, deposit, amount, region, create_date, last_update) VALUES ( TO_NUMBER(S.customer_id), -- 类型转换 S.customer_name, S.deposit, S.amount, S.region, S.create_date, SYSDATE ); ``` ### 关键处理技术 1. **跨数据库连接** - 使用 PostgreSQL 的 `dblink` 扩展直接访问 Oracle 数据 - 避免中间文件传输,提升迁移效率[^1] 2. **数据类型转换** ```sql -- PostgreSQL 端 customer_id::text -- 显式转为文本 -- Oracle 端 TO_NUMBER(S.customer_id) -- 文本转数字 ``` - 解决 PostgreSQL 的 `SERIAL` 与 Oracle `NUMBER` 类型差异[^4] 3. **条件过滤** - `WHERE last_update > CURRENT_DATE - INTERVAL '30 days'` 仅迁移增量数据 - `WHERE S.region = 'EAST'` 限制更新范围 4. **性能优化** ```sql -- Oracle 端创建索引 CREATE INDEX idx_cust_merge ON customers_hist(customer_id); -- PostgreSQL 端优化查询 ANALYZE pg_customers; ``` ### 替代方案:使用中间文件 若无法使用 `dblink`,可通过 CSV 中转: ```bash # PostgreSQL 导出 psql -c "\copy (SELECT * FROM pg_customers) TO 'data.csv' WITH CSV HEADER" # Oracle SQL*Loader 控制文件 LOAD DATA INFILE 'data.csv' APPEND INTO TABLE customers_hist FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' (customer_id, customer_name, deposit, amount, region, create_date "YYYY-MM-DD", last_date "YYYY-MM-DD") ``` ### 迁移注意事项 1. **事务控制** ```sql BEGIN MERGE INTO ...; COMMIT; -- 显式提交事务 EXCEPTION WHEN OTHERS THEN ROLLBACK; RAISE; END; ``` 2. **错误日志记录** ```sql -- Oracle 启用错误日志 EXEC DBMS_ERRLOG.CREATE_ERROR_LOG('customers_hist', 'err_customers'); MERGE INTO customers_hist LOG ERRORS INTO err_customers ('MERGE_MIGRATION') REJECT LIMIT UNLIMITED; ``` 3. **性能监控** ```sql -- 检查执行计划 EXPLAIN PLAN FOR MERGE INTO ...; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY); ``` > **重要提示**:对于国产化迁移场景(如 KingbaseES),语法兼容性更高,可直接使用 Oracle 风格的 MERGE 语句[^4][^5]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值