Hvis du ville have en massiv problemer med din tilgang, mangler du sandsynligvis et indeks i kolonnen clean.id
, som er påkrævet for din tilgang, når MERGE
bruger dual
som kilde for hver række.
Dette er mindre sandsynligt, mens du siger id
er en primær nøgle .
Så dybest set du gør det rigtige, og du vil se udførelsesplan ligner den nedenfor:
---------------------------------------------------------------------------------------------------
| 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)
Så udførelsesplanen er fin og fungerer effektivt, men den har et problem.
Husk du altid bruger et indeks, du vil være glad, mens du behandler få rækker, men det vil ikke skalere .
Hvis du behandler en millioner af poster, kan du falde tilbage til en totrinsbehandling,
-
indsæt alle rækker i en midlertidig tabel
-
udføre en enkelt
MERGE
sætning ved hjælp af den midlertidige tabel
Den store fordel er, at Oracle kan åbne en hash join
og slippe af med indeksadgangen for hver af millionerne rækker.
Her et eksempel på en test af clean
tabel initieret med 1M id
(ikke vist) og udfører 1M insert og 1M opdateringer:
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)
Testen løber i ca. 10 sekunder, hvilket er mindre end halvdelen af dig nærmer dig med MERGE
ved hjælp af dual
.
Hvis dette stadig ikke er nok, bliver du nødt til at bruge parallel mulighed .