web-dev-qa-db-fra.com

Insertion en bloc dans la base de données Oracle: Quel est le meilleur: boucle de curseur FOR ou simple Select?

Quelle serait la meilleure option pour une insertion en masse dans une base de données Oracle? Une boucle FOR Cursor comme

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

ou une simple sélection, comme:

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

Une raison particulière, que ce soit meilleur ou pas?

24
Sathyajith Bhat

Je recommanderais l'option Select car les curseurs prennent plus de temps.
L’utilisation de Select est également beaucoup plus facile à comprendre pour quiconque doit modifier votre requête

28
Josh Mein

La règle générale est, si vous pouvez le faire en utilisant une seule instruction SQL au lieu d'utiliser PL/SQL, vous devriez. Ce sera généralement plus efficace.

Toutefois, si vous devez ajouter davantage de logique procédurale (pour une raison quelconque), vous devrez peut-être utiliser PL/SQL, mais vous devrez utiliser des opérations en bloc au lieu d'un traitement ligne par ligne. (Remarque: dans Oracle 10g et versions ultérieures, votre boucle FOR utilisera automatiquement BULK COLLECT pour extraire 100 lignes à la fois; toutefois, votre instruction d'insertion sera toujours exécutée ligne par ligne).

par exemple.

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;

Ce qui précède présente l'avantage de minimiser les basculements de contexte entre SQL et PL/SQL. Oracle 11g prend également mieux en charge les tables d’enregistrement, de sorte que vous n’avez pas besoin d’une table PL/SQL distincte pour chaque colonne.

De plus, si le volume de données est très important, il est possible de modifier le code pour traiter les données par lots.

21
Jeffrey Kemp

Si votre segment d'annulation/segment d'annulation peut accepter la taille de la transaction, l'option 2 est préférable. L'option 1 est utile si vous ne disposez pas de la capacité de restauration nécessaire et que vous devez diviser le gros insert en commits plus petits afin d'éviter des erreurs de segmentation/annulation trop faibles. 

5
MichaelN

Une simple insertion/sélection comme votre deuxième option est de loin préférable. Pour chaque insertion dans la 1ère option, vous devez utiliser un commutateur de contexte de pl/sql à sql. Exécutez chacun avec trace/tkprof et examinez les résultats.

Si, comme le mentionne Michael, votre retour en arrière ne peut pas traiter la déclaration, demandez à votre dba de vous en donner plus. Le disque n'est pas cher, tandis que les résultats partiels résultant de l'insertion de vos données en plusieurs passes sont potentiellement assez coûteux. (Il n'y a presque aucune annulation associée à un insert.)

5
Scott Swank

Je pense que dans cette question il manque une information importante.

Combien de disques allez-vous insérer?

  1. Si de 1 à cca. 10.000, vous devez alors utiliser l'instruction SQL (comme ils l'ont dit, il est facile à comprendre et à écrire).
  2. Si de cca. 10.000 à cca. 100.000, vous devez alors utiliser le curseur, mais vous devez ajouter une logique à valider tous les 10 000 enregistrements. 
  3. Si de cca. 100 000 à des millions, alors vous devriez utiliser la collecte en masse pour de meilleures performances.
3
sulica

Comme vous pouvez le constater en lisant les autres réponses, de nombreuses options sont disponibles. Si vous ne faites que <10 000 lignes, vous devriez choisir la deuxième option. 

En bref, pour environ> 10k jusqu’à dire <100k. C'est une sorte de zone grise. Beaucoup de vieux geezers vont aboyer sur de gros segments de rollback. Mais honnêtement, le matériel et les logiciels ont fait d’énormes progrès: vous pouvez peut-être utiliser l’option 2 pour de nombreux enregistrements si vous n’exécutez le code que quelques fois. Sinon, vous devriez probablement valider chaque rangée d'environ 1 à 10k. Voici un extrait que j'utilise. J'aime ça parce que c'est court et que je n'ai pas à déclarer un curseur. De plus, il présente les avantages de la collecte en bloc et pour tous.

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;

J'ai trouvé ce link sur le site Oracle qui illustre les options plus en détail.

2
Arturo Hernandez

Je ne fais ni l'un ni l'autre pour un rechargement quotidien complet des données. Par exemple, je charge mon site de Denver. Il existe d'autres stratégies pour les deltas en temps quasi réel.

Comme je l'ai constaté, j'utilise une table SQL de création, qui est presque aussi rapide qu'un chargement en bloc Par exemple, une instruction create table est utilisée ci-dessous pour organiser les données, en transformant les colonnes en un type de données approprié:

CREATE TABLE sales_dataTemp en tant que sélection Cast (colonne1 en tant que date) en tant que SALES_QUARTER, Cast (en tant que nombre) en tant que SALES_IN_MILLIONS, .... FROM TABLEAU 1;

cette table temporaire reflète exactement la structure de ma table cible, qui est la liste partitionnée par le site .Je fais ensuite un échange de partition avec la partition DENVER et j'ai un nouvel ensemble de données.

0
Hughsmg

Vous pouvez utiliser:

Collecte en bloc avec FOR ALL appelée Bulk binding

Étant donné que l'opérateur forall de PL/SQL est 30 fois plus rapide pour les insertions de table simples. 

BULK_COLLECT et Oracle FORALL ensemble, ces deux fonctionnalités sont appelées Bulk Binding. Les liaisons en bloc sont une technique PL/SQL où, au lieu de plusieurs instructions individuelles SELECT, INSERT, UPDATE ou DELETE, sont exécutées pour extraire ou stocker des données dans une table, toutes les opérations sont effectuées simultanément, en bloc. Cela évite le changement de contexte que vous obtenez lorsque le moteur PL/SQL doit passer au moteur SQL, puis de nouveau au moteur PL/SQL, et ainsi de suite, lorsque vous accédez individuellement aux lignes une à une. Pour effectuer des liaisons en bloc avec les instructions INSERT, UPDATE et DELETE, vous placez l’instruction SQL dans une instruction PL/SQL FORALL. Pour effectuer des liaisons en bloc avec les instructions SELECT, vous devez inclure la clause BULK COLLECT dans l'instruction SELECT au lieu d'utiliser INTO.

Cela améliore les performances.

0
user2001117