Bulk Collect ve FORALL Kullanımı

Aktif kullanılan tabloya en hızlı şekilde ve tabloyu kilitlemeden arşiv logu üretmeden hafıza ve undo alanlarını doldurmadan nasıl büyük veriyi ekleyebiliriz?


Oracle 'da bunun için BULK COLLECT ve FORALL kullanarak yapabiliriz.

Arşiv log üretmemek için öncelikle INSERT yapılacak tablonun bulunduğu alanı archive log üretmemesi için düzenliyoruz.

ALTER TABLESPACE <TABLESPACE_NAME> NOLOGGING;

Aşağıdaki prosedürü oluşturarak çalıştırabilirsiniz.


CREATE OR REPLACE PROCEDURE <SCHEMA>.<PROCEDURE_NAME>
  IS  
   TYPE fetch1 IS TABLE OF <TABLE_NAME>%ROWTYPE;
   l_array   fetch1;
   CURSOR cur_1 IS SELECT * FROM <TABLE_NAME>;
   l_start  NUMBER;
BEGIN
   l_start := DBMS_UTILITY.get_time;
   DBMS_OUTPUT.PUT_LINE('Time Started : ' || TO_CHAR(SYSDATE, 'DD-MON-YYYY HH24:MI:SS'));
   OPEN cur_1;
   LOOP
        FETCH cur_1 BULK COLLECT INTO l_array LIMIT 100000;
        DBMS_OUTPUT.put_line('Collected : ' || (DBMS_UTILITY.get_time - l_start));
        EXIT WHEN l_array.count = 0;
        l_start := DBMS_UTILITY.get_time;
     FORALL i IN l_array.FIRST .. l_array.LAST SAVE EXCEPTIONS
          INSERT /*+ APPEND */ INTO <TABLE_NAME>  VALUES l_array(i);
     COMMIT;
     DBMS_OUTPUT.PUT_LINE('100000 Rows Inserted : ' || (DBMS_UTILITY.get_time - l_start));
   END LOOP;
   DBMS_OUTPUT.PUT_LINE('Time Finished : ' || TO_CHAR(SYSDATE, 'DD-MON-YYYY HH24:MI:SS'));
   CLOSE cur_1;
EXCEPTION
    WHEN NO_DATA_FOUND
    THEN
        DBMS_OUTPUT.put_line (SQLERRM);
    WHEN OTHERS
    THEN
        DBMS_OUTPUT.put_line ('Rows inserted : ' || SQL%ROWCOUNT);
        DBMS_OUTPUT.put_line('Number of failures: ' || SQL%BULK_EXCEPTIONS.count);
        DBMS_OUTPUT.put_line (SQLERRM);
        FOR indx IN 1 .. SQL%BULK_EXCEPTIONS.COUNT
      LOOP
         DBMS_OUTPUT.put_line ('Error '|| indx || ' occurred on index '
            || SQL%BULK_EXCEPTIONS (indx).ERROR_INDEX
            || ' attempting to update name to "'
            || SQL%BULK_EXCEPTIONS (indx).ERROR_INDEX || '"');
         DBMS_OUTPUT.put_line (
               'Oracle error is : '
            || SQLERRM(-SQL%BULK_EXCEPTIONS(indx).ERROR_CODE));
               
      END LOOP;
      ROLLBACK;
END;


Tabloların ve prosedürün aynı şemada olmasına dikkat ediniz! Çünkü "TYPE fetch1 IS TABLE OF <TABLE_NAME>%ROWTYPE;" kısmında şema ismini giremiyorsunuz sadece tablo ismi girebiliyorsunuz.Eğer aynı yerde olamayacak ise aşağıdaki komutu da çalıştırmayı yada prosedürünüze eklemeyi unutmayın.

ALTER SESSION SET CURRENT_SCHEMA=<SCHEMA_NAME>;
Ben bu procedürü bir partition için 200 milyondan fazla kayıt INSERT edeceğim için işlem günlerce sürdü.İşlemin bu günler içinde kesintiye uğramaması için ben DBMS_SCHEDULER_JOB kullanarak sunucu tarafında arka planda çalıştırdım.Günlerce sürecek işlem için direkt bilgisayarınızdan değilde sunucu taraflı çalıştırmayı tercih etmelisiniz.


Diğer bir konuda APPEND_VALUES hint yerine ben APPEND kullandım.APPEND_VALUES ile kullanırken aktif kullanılan tabloyu kilitlediğini uygulamanın çalışamaz duruma geldiğini farkettim.Aradaki farkları araştırarak siz amacınıza uygun olanı tercih edebilirsiniz.


Kaynaklar :

Yorum Gönder

Daha yeni Daha eski