2011-11-01 3 views
3

J'ai la procédure suivante qui remplit des valeurs nulles dans une colonne. La procédure fonctionne bien si j'ai un très petit ensemble de données. Mais les données que je cible sont d'environ 3 milliards d'enregistrements. Le simple fait de tester ce script sur un disque de 1 million a déclenché ces exécutions.Dépassement du tampon de procédure

ORA-20000: ORU-10027: buffer overflow, limit of 20000 bytes 
ORA-06512: at "SYS.DBMS_OUTPUT", line 32 
ORA-06512: at "SYS.DBMS_OUTPUT", line 97 
ORA-06512: at "SYS.DBMS_OUTPUT", line 112 
ORA-06512: at "DBNAME.PRBACKFILLI", line 39 
ORA-06512: at line 2 

Après avoir un creusement petit peu, je réalise que la production DBMS_OUTPUT.PUT_LINE imprime à la fin de la procédure. Maintenant, la chose est que nous voulons des informations de débogage, que devons-nous faire?

CREATE OR REPLACE PROCEDURE PRBACKFILL (str_dest IN VARCHAR2) AS 
    CURSOR cr_pst_ IS 
    select id, seq from TABLE_ where ID is null; 

    TYPE t_id_array IS TABLE OF NUMBER INDEX BY BINARY_INTEGER; 
    TYPE t_seq_array IS TABLE OF NUMBER INDEX BY BINARY_INTEGER; 

    a_id t_id_array; 
    a_seq t_seq_array; 
    i_bulk_limit NUMBER := 1000; 
BEGIN 
    OPEN cr_pst_; 
    LOOP 
    FETCH cr_pst_ 
    BULK COLLECT INTO a_id, a_seq LIMIT i_bulk_limit; 


    FOR i IN 1..a_id.count LOOP 
     a_id(i) := Floor(a_seq(i)/10000000000000); 
    END LOOP; 

    FORALL i IN 1 .. a_id.count 
     UPDATE TABLE_ 
     SET ID = a_id(i) 
     WHERE SEQ = a_seq(i); 

     COMMIT; 
     DBMS_OUTPUT.PUT_LINE ('COMMITED '||i_bulk_limit||' records'); 
    EXIT WHEN cr_pst_%NOTFOUND; 
    END LOOP; -- main cursor loop 
    CLOSE cr_pst_; 

    DBMS_OUTPUT.PUT_LINE ('Backfill completed gracefully!'); 

    EXCEPTION 
    WHEN NO_DATA_FOUND THEN 
     DBMS_OUTPUT.PUT_LINE('No more records to process'); 
    WHEN OTHERS THEN 
     DBMS_OUTPUT.PUT_LINE('errno: '||TO_CHAR(SQLCODE)||' Msg: ' || SQLERRM);    
END PRBACKFILL; 
. 
/
sho err; 
+0

Voir aussi http://stackoverflow.com/questions/8045844/dbms-output-size-buffer-overflow – Vadzim

Répondre

11

Tout d'abord, vous n'utiliserez normalement pas DBMS_OUTPUT pour la journalisation. Il est généralement plus logique d'écrire les données dans une table de journal, en particulier si votre procédure de journalisation a été définie comme une transaction autonome afin que vous puissiez surveiller les données du journal pendant l'exécution de la procédure. DBMS_OUTPUT ne sera affiché qu'après la fin de l'exécution complète de la procédure, moment auquel il est généralement inutile. En relation avec ce premier point, il est très difficile de s'appuyer sur DBMS_OUTPUT pour indiquer à l'appelant qu'il y a eu une sorte d'exception. Au minimum, vous voudriez re-augmenter l'exception qui a été lancée afin que vous obteniez la pile d'erreur afin de déboguer le problème. Deuxièmement, lorsque vous activez la sortie, vous devez spécifier la taille de la mémoire tampon à laquelle DBMS_OUTPUT peut écrire. Il semble que vous avez déclaré que le tampon soit 20.000 octets qui est la valeur par défaut si vous simplement

SQL> set serveroutput on; 

Vous pouvez changer cela en spécifiant une taille, mais la taille maximale est limitée à 1.000.000 octets

SQL> set serveroutput on size 1000000; 

Si vous prévoyez de mettre à jour 3 milliards de lignes dans des blocs de 1000 lignes, cela va être un tampon beaucoup trop petit. Vous allez générer plus de 60 fois cette quantité de données avec votre code actuel. Si vous utilisez 10.2 à la fois sur le client et sur le serveur, vous devriez être en mesure d'allouer un tampon illimité

SQL> set serveroutput on size unlimited; 

mais ce n'est pas une option dans les versions précédentes. Enfin, êtes-vous certain que vous avez besoin de recourir à PL/SQL en priorité? Il semble que vous pourriez le faire plus efficacement en exécutant tout simplement un jour unique

UPDATE table_ 
    SET id = floor(seq/ 10000000000000) 
WHERE id is null; 

C'est beaucoup moins de code, beaucoup plus facile à lire, et sera plus efficace que l'alternative PL/SQL. Le seul inconvénient est qu'il nécessite que votre tablespace UNDO soit assez grand pour accueillir l'UNDO qui est généré mais la mise à jour d'une seule colonne de NULL à une valeur numérique non NULL ne devrait pas générer autant de UNDO.

+0

J'ai écrit la logique simplifiée. L'instruction UPDATE ne fonctionnerait pas. –

+1

Je ne suis pas sûr du plus tôt un tampon illimité était disponible, mais c'était une option dans 10g R2. Référence SQL * Plus: http://download.oracle.com/docs/cd/B19306_01/server.102/b14357/ch12040.htm#sthref2862, DBMS_OUTPUT.ENABLE: http://download.oracle.com/docs/cd /B19306_01/appdev.102/b14258/d_output.htm#i999293 –

+0

@ShannonSeverance - Merci pour le crochet. On dirait qu'il a été ajouté en 10.2 car il n'y a pas de référence dans la documentation 10.1 http://download.oracle.com/docs/cd/B14117_01/server.101/b12170/ch13.htm # sthref2817 J'ai mis à jour ma réponse. –

Questions connexes