2009-06-12 11 views
22

Quale sarebbe l'opzione migliore per l'inserimento di massa in un database Oracle? un ciclo for Cursore comeInserimento di massa nel database Oracle: quale è meglio: FOR Cursor loop o un semplice Select?

DECLARE 
    CURSOR C1 IS SELECT * FROM FOO; 
BEGIN 
    FOR C1_REC IN C1 LOOP 
    INSERT INTO BAR(A, 
       B, 
       C) 
      VALUES(C1.A, 
       C1.B, 
       C1.C); 
    END LOOP; 
END 

o un semplice selezionare, come:

INSERT INTO BAR(A, 
       B, 
       C) 
     (SELECT A, 
       B, 
       C 
     FROM FOO); 

Qual è il motivo specifico uno dei due sarebbe meglio?

+2

FYI, non si dovrebbe fare riferimento a questo come un "inserto di massa". Mentre potresti inserire grandi quantità di dati, c'è una funzione PL/SQL chiamata Bulk Operations (Bulk Inserts, Bulk Collect) ecc. – moleboy

+0

E per aggiungere al riferimento alle operazioni bulk, dovresti considerare che al di sopra degli altri due per le operazioni con i dati di grandi dimensioni o in corso, operazioni di inserimento regolari. Gli inserti di massa possono produrre enormi miglioramenti delle prestazioni rispetto ai semplici inserti negli esempi forniti. – DCookie

risposta

25

Consiglierei l'opzione Seleziona perché i cursori richiedono più tempo.
Anche utilizzando il Select è molto più facile da capire per chi ha di modificare la query

+6

+1. La seconda opzione è anche più concisa e non richiede la compilazione/esecuzione di un blocco PL/SQL. –

5

Se il proprio segmento di rollback/undo segmento può accogliere la dimensione della transazione, allora l'opzione 2 è migliore. L'opzione 1 è utile se non si ha la capacità di rollback necessaria e si deve rompere l'inserto grande in commit più piccoli in modo da non ottenere errori di rollback/annullamento del segmento troppo piccoli.

5

Un semplice inserimento/selezione come la seconda opzione è preferibile. Per ogni inserto nella prima opzione è necessario un cambio di contesto da pl/sql a sql. Esegui ognuno con trace/tkprof ed esamina i risultati.

Se, come dice Michael, il tuo rollback non può gestire l'affermazione, allora il tuo dba ti darà di più. Il disco è economico, mentre i risultati parziali derivanti dall'inserimento dei dati in più passaggi sono potenzialmente piuttosto costosi. (Non c'è quasi nessun annullamento associato a un inserto.)

21

La regola generale è, se è possibile farlo utilizzando una singola istruzione SQL invece di utilizzare PL/SQL, è necessario. Di solito sarà più efficiente.

Tuttavia, se è necessario aggiungere più logica procedurale (per qualche motivo), potrebbe essere necessario utilizzare PL/SQL, ma è necessario utilizzare le operazioni di massa anziché l'elaborazione riga per riga. (Nota: in Oracle 10g e versioni successive, il ciclo FOR utilizzerà automaticamente BULK COLLECT per recuperare 100 righe alla volta, tuttavia l'istruzione di inserimento verrà comunque eseguita riga per riga).

ad es.

DECLARE 
    TYPE tA IS TABLE OF FOO.A%TYPE INDEX BY PLS_INTEGER; 
    TYPE tB IS TABLE OF FOO.B%TYPE INDEX BY PLS_INTEGER; 
    TYPE tC IS TABLE OF FOO.C%TYPE INDEX BY PLS_INTEGER; 
    rA tA; 
    rB tB; 
    rC tC; 
BEGIN 
    SELECT * BULK COLLECT INTO rA, rB, rC FROM FOO; 
    -- (do some procedural logic on the data?) 
    FORALL i IN rA.FIRST..rA.LAST 
     INSERT INTO BAR(A, 
         B, 
         C) 
     VALUES(rA(i), 
      rB(i), 
      rC(i)); 
END; 

L'ha sopra il vantaggio di minimizzare contesto commuta tra SQL e PL/SQL. Oracle 11g offre inoltre un supporto migliore per le tabelle di record, in modo da non dover disporre di una tabella PL/SQL separata per ogni colonna.

Inoltre, se il volume dei dati è molto elevato, è possibile modificare il codice per elaborare i dati in lotti.

3

Penso che in questa domanda manchi una informazione importante.

Quanti dischi inserirai?

  1. Se da 1 a cca. 10.000 quindi dovresti usare l'istruzione SQL (come hanno detto è facile da capire ed è facile da scrivere).
  2. Se da cca. 10.000 a cca. 100.000 quindi dovresti usare il cursore, ma dovresti aggiungere la logica per impegnarti su ogni 10.000 record.
  3. Se da cca. Da 100.000 a milioni, dovresti usare bulk collect per ottenere prestazioni migliori.
2

Come puoi vedere leggendo le altre risposte, ci sono molte opzioni disponibili. Se stai facendo solo le righe < 10k dovresti andare con la seconda opzione.

In breve, per circa> 10 k tutto il senso di dire un < 100k. È una specie di area grigia. Molti vecchi guerrieri abbaiano a grandi segmenti di rollback. Ma onestamente, l'hardware e il software hanno fatto progressi incredibili in cui potresti riuscire a scappare con l'opzione 2 per molti record se esegui il codice solo poche volte. Altrimenti dovresti probabilmente commettere ogni 1k-10k o più righe. Ecco un frammento che uso. Mi piace perché è breve e non devo dichiarare un cursore. Inoltre ha i vantaggi della raccolta collettiva e di tutti i giorni.

begin 
    for r in (select rownum rn, t.* from foo t) loop 
     insert into bar (A,B,C) values (r.A,r.B,r.C); 
     if mod(rn,1000)=0 then 
      commit; 
     end if; 
    end; 
    commit; 
end; 

ho trovato questo link dal sito oracolo che illustra le opzioni in modo più dettagliato.

+0

grazie per l'intuizione! – Sathya

0

È possibile utilizzare:

Bulk raccogliere insieme per tutto ciò che si chiama Bulk binding.

Dato che l'operatore PL/SQL forall ha una velocità di 30 volte più veloce per inserti di tabella semplici.

BULK_COLLECT e Oracle FORALL insieme queste due funzionalità sono note come Bulk Binding. Bulk Binds sono una tecnica PL/SQL in cui, anziché più singole singole SELECT, INSERT, UPDATE o DELETE le istruzioni vengono eseguite per recuperare da, o memorizzare i dati nella tabella, tutte le operazioni vengono eseguite contemporaneamente, alla rinfusa. Ciò evita il cambio di contesto che si ottiene quando il motore PL/SQL deve passare al motore SQL, quindi di nuovo al motore PL/SQL e così via, quando si accede individualmente alle righe una alla volta. Per eseguire il bulk binding con le dichiarazioni INSERT, UPDATE e DELETE, si racchiude l'istruzione SQL all'interno di un'istruzione PL/SQL FORALL. Per eseguire binding collettivi con le istruzioni SELECT, includere la clausola BULK COLLECT nell'istruzione SELECT invece di utilizzare INTO.

Migliora le prestazioni.

Problemi correlati