Oracle
 sql >> Database >  >> RDS >> Oracle

C'è un modo per migliorare una query MERGE?

Se dovessi avere un massiccio problemi con il tuo approccio, molto probabilmente ti manca un indice nella colonna clean.id , necessario per il tuo approccio quando MERGE utilizza dual come fonte per ogni riga.

Questo è meno probabile mentre dici l'id è una chiave primaria .

Quindi fondamentalmente stai pensando nel modo giusto e vedrai il piano di esecuzione simile a quello qui sotto:

---------------------------------------------------------------------------------------------------
| 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)

Quindi il piano di esecuzione va bene e funziona in modo efficace, ma presenta un problema.

Ricorda usa sempre un indice, sarai felice mentre elabori poche righe, ma non verrà ridimensionato .

Se stai elaborando milioni di record, potresti ricorrere a un'elaborazione in due fasi,

  • inserisci tutte le righe in una tabella temporanea

  • eseguire un singolo MERGE istruzione utilizzando la tabella temporanea

Il grande vantaggio è che Oracle può aprire un hash join ed elimina l'accesso all'indice per ciascuno dei milioni righe.

Ecco un esempio di un test di clean tabella iniziata con 1M id (non mostrato) ed eseguendo 1M di inserimento e 1M di aggiornamenti:

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)

Il test viene eseguito in ca. 10 secondi, che è meno della metà di te che ti avvicini con MERGE usando dual .

Se questo non è ancora sufficiente, dovrai utilizzare l'opzione parallela .