Si tuvieras un masivo problemas con su enfoque, es muy probable que le falte un índice en la columna clean.id
, que se requiere para su enfoque cuando MERGE
usa dual
como fuente para cada fila.
Esto es menos probable mientras dices el id
es una clave principal .
Así que básicamente estás pensando bien y verás plan de ejecución similar a la siguiente:
---------------------------------------------------------------------------------------------------
| 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)
Entonces, el plan de ejecución está bien y funciona de manera efectiva, pero tiene un problema.
Recuerde siempre use un índice, estará feliz mientras procesa algunas filas, pero no escalará .
Si está procesando un millones de registros, puede recurrir a un procesamiento de dos pasos,
-
insertar todas las filas en una tabla temporal
-
realizar un solo
MERGE
declaración usando la tabla temporal
La gran ventaja es que Oracle puede abrir una hash join
y deshacerse del acceso al índice para cada uno de los millones filas.
Aquí un ejemplo de una prueba del clean
tabla iniciada con 1M id
(no se muestra) y realizando 1M de inserción y 1M de actualizaciones:
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)
La prueba se ejecuta en aprox. 10 segundos, que es menos de la mitad de lo que te acercas con MERGE
usando dual
.
Si esto aún no es suficiente, deberá usar la opción paralela .