Se você tivesse um enorme problemas com sua abordagem, você provavelmente está perdendo um índice na coluna
clean.id
, que é necessário para sua abordagem quando o MERGE
usa dual
como uma fonte para cada linha. Isso é menos provável enquanto você está dizendo o
id
é uma chave primária . Então basicamente você está pensando corretamente e verá o plano de execução semelhante ao abaixo:
---------------------------------------------------------------------------------------------------
| 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)
Portanto, o plano de execução é bom e funciona de forma eficaz, mas tem um problema.
Lembre-se você sempre usa um índice, você ficará feliz ao processar algumas linhas, mas ele não será dimensionado .
Se você estiver processando milhões de registros, você pode recorrer a um processamento em duas etapas,
-
inserir todas as linhas em uma tabela temporária
-
execute um únicoMERGE
declaração usando a tabela temporária
A grande vantagem é que o Oracle pode abrir um
hash join
e livre-se do acesso ao índice para cada um dos milhões linhas. Aqui um exemplo de um teste do
clean
tabela iniciada com 1 milhão de id
(não mostrado) e realizando 1M de inserção e 1M de atualizações: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)
O teste é executado em aprox. 10 segundos, que é menos da metade de sua abordagem com
MERGE
usando dual
. Se isso ainda não for suficiente, você terá que usar a opção paralela .