2015-09-23

Temporary lobs

Встала задачка собрать много строчек таблицы в один BLOB.
Краткий алгоритм как это сделать: делаем temporary lob - изменяем его, вставляя строки - вставляем - очищаем - возвращаемся к шагу 1.
В нижележащих тестах разница, на сколько отличается по времени работа с temporary lob, созданного с параметром cache = true и cache = false
Результаты: cache = true в 20 раз быстрее
Код и результата теста

SET SERVEROUTPUT ON

PROMPT CACHE=TRUE

DECLARE
  bl BLOB;
  l_raw RAW(32767);
  l_temp_cnt NUMBER;
  l_start_time NUMBER := dbms_utility.get_time;
  FUNCTION get_temp_blocks_cnt RETURN NUMBER IS
    l_cnt NUMBER;
  BEGIN
    SELECT SUM(u.blocks) 
    INTO l_cnt
    FROM v$tempseg_usage u, v$session s
    WHERE u.session_addr = s.saddr AND AUDSID = USERENV('SESSIONID');
    RETURN NVL(l_cnt, 0);
  END;
BEGIN
  l_temp_cnt := get_temp_blocks_cnt;
  DBMS_LOB.createtemporary(bl, TRUE);

  FOR i IN 1 .. 100000 LOOP
    l_raw := utl_raw.cast_to_raw(RPAD(i, 1000, ' '));
    dbms_lob.writeappend(bl, utl_raw.length(l_raw), l_raw);
    IF MOD(i, 10) = 0 THEN 
      dbms_lob.freetemporary(lob_loc => bl);
      DBMS_LOB.createtemporary(bl, TRUE);
    END IF;
  END LOOP; 

  dbms_output.put_line('Temp used=' || (get_temp_blocks_cnt - l_temp_cnt)); 
  dbms_output.put_line('Time used ' || (dbms_utility.get_time - l_start_time) / 100); 
END;
/

PROMPT CACHE=FALSE

DECLARE
  bl BLOB;
  l_raw RAW(32767);
  l_temp_cnt NUMBER;
  l_start_time NUMBER := dbms_utility.get_time;
  FUNCTION get_temp_blocks_cnt RETURN NUMBER IS
    l_cnt NUMBER;
  BEGIN
    SELECT SUM(u.blocks) 
    INTO l_cnt
    FROM v$tempseg_usage u, v$session s
    WHERE u.session_addr = s.saddr AND AUDSID = USERENV('SESSIONID');
    RETURN NVL(l_cnt, 0);
  END;
BEGIN
  l_temp_cnt := get_temp_blocks_cnt;
  DBMS_LOB.createtemporary(bl, FALSE);

  FOR i IN 1 .. 100000 LOOP
    l_raw := utl_raw.cast_to_raw(RPAD(i, 1000, ' '));
    dbms_lob.writeappend(bl, utl_raw.length(l_raw), l_raw);
    IF MOD(i, 10) = 0 THEN 
      dbms_lob.freetemporary(lob_loc => bl);
      DBMS_LOB.createtemporary(bl, FALSE);
    END IF;
  END LOOP; 

  dbms_output.put_line('Temp used=' || (get_temp_blocks_cnt - l_temp_cnt)); 
  dbms_output.put_line('Time used ' || (dbms_utility.get_time - l_start_time) / 100); 
END;
/  

CACHE=TRUE
Temp used=0
Time used 2.08
PL/SQL procedure successfully completed
CACHE=FALSE
Temp used=0
Time used 42.29
PL/SQL procedure successfully completed

Примечательно, что единственный способ, который сработал для очистки BLOB это

      dbms_lob.freetemporary(lob_loc => bl);
      DBMS_LOB.createtemporary(bl);

Ни присвоение NULL, ни empty_blob, который работает только для insert и select, как оказалось из найденной документации от версии 8.1

An exception is raised if you use these functions anywhere but in the VALUES clause of a SQL INSERT statement or as the source of the SET clause in a SQL UPDATE statement.
А для свежих версий
You cannot use the locator returned from this function as a parameter
to the DBMS_LOB package or the OCI.

Для дальнейших исследований можно заняться замерами потребляемой памяти.

Комментариев нет: