如果您的方法遇到大量 问题,您很可能在clean.id 列上缺少索引,当MERGE 使用dual 时,这是您的方法所必需的作为每一行的来源。
当您说id 是一个主键时,这不太可能。
所以基本上你的想法是正确的,你会看到execution plan类似于下面的:
---------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------
| 0 | MERGE STATEMENT | | | | 2 (100)| |
| 1 | MERGE | CLEAN | | | | |
| 2 | VIEW | | | | | |
| 3 | NESTED LOOPS OUTER | | 1 | 40 | 2 (0)| 00:00:01 |
| 4 | TABLE ACCESS FULL | DUAL | 1 | 2 | 2 (0)| 00:00:01 |
| 5 | VIEW | VW_LAT_A18161FF | 1 | 38 | 0 (0)| |
| 6 | TABLE ACCESS BY INDEX ROWID| CLEAN | 1 | 38 | 0 (0)| |
|* 7 | INDEX UNIQUE SCAN | CLEAN_UX1 | 1 | | 0 (0)| |
---------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
7 - access("CLEAN"."ID"=:ID)
所以执行计划很好并且有效,但是它有一个问题。
请记住始终使用索引,在处理几行时您会很高兴,但它不会扩展。
如果您正在处理 数百万 条记录,您可能会退回到两步处理,
-
在临时表中插入所有行
-
使用临时表执行单个MERGE 语句
最大的优势是 Oracle 可以打开一个hash join 并摆脱对每一百万行的索引访问。
这里是一个测试 clean 表的示例,该表使用 1M id(未显示)启动并执行 1M 插入和 1M 更新:
n = 1000000
data2 = [{"id" : i, "xcount" :1} for i in range(2*n)]
sql3 = """
insert into tmp (id,count)
values (:id,:xcount)"""
sql4 = """MERGE into clean USING tmp on (clean.id = tmp.id)
when not matched then insert (id, count) values (tmp.id, tmp.count)
when matched then update set clean.count= clean.count + tmp.count"""
cursor.executemany(sql3, data2)
cursor.execute(sql4)
测试大约运行。 10 秒,不到一半的人使用MERGE使用dual。
如果这还不够,您将不得不使用并行选项。