Отправляет email-рассылки с помощью сервиса Sendsay

Килограмм килобайтов

  Все выпуски  

Килограмм килобайтов


"Килограмм килобайтов"


Выпуск №43 Дата выхода:2008-12-09
Сайт рассылки: Рациональное программирование

Документация
 
Программирование
Java
PHP
Oracle
Операционные системы
FreeBSD
Linux
QNX
Windows

Здравствуйте, уважаемые подписчики

Продолжаю публикацию статьи об использовании выражения BULK COLLECT в Oracle.
Вторая часть.

Извлечение результатов выборки в коллекции, используя выражение BULK COLLECT.

Использование ключевых слов BULK COLLECT в выборках - очень эффективный способ получения результирующих данных. Вместо циклической обработки каждого ряда, Вы сохраняете результат в одной или нескольких коллекциях, все это делается в рамках одной операцией. Это ключевое слово может использоваться совместно с выражениями SELECT INTO, FETCH INTO и RETURNING INTO.

При использовании ключевых слов BULK COLLECT все переменные в списке INTO должны быть коллекциями. Колонки таблицы могут быть как скалярными значениями так и структурами, включая объектные типы.

Пример


DECLARE

  TYPE NumTab IS TABLE OF employees.employee_id%TYPE;
  TYPE NameTab IS TABLE OF employees.last_name%TYPE;
  enums NumTab;   -- Нет необходимости инициализировать коллекцию.
  names NameTab; -- Значения будут заполнены выражением SELECT INTO.

PROCEDURE print_results IS
BEGIN
  IF enums.COUNT = 0 THEN
     DBMS_OUTPUT.PUT_LINE('No results!');
  ELSE
    DBMS_OUTPUT.PUT_LINE('Results:');
    FOR i IN enums.FIRST..enums.LAST
    LOOP
       DBMS_OUTPUT.PUT_LINE(' Employee #' || enums(i) || ': ' || names(i));
    END LOOP;
  END IF;
END;

BEGIN

-- Извлечение данных по сотрудникам, идентификатор которых больше 1000

SELECT employee_id, last_name BULK COLLECT 
     INTO  enums, names FROM employees 
  WHERE employee_id > 1000;

-- Все данные помещены в память выражением BULK COLLECT
-- Нет необходимости выполнять FETCH для каждого ряда результирующих данных

print_results();

-- Выборка приблизительно 20% всех рядов

SELECT employee_id, last_name BULK COLLECT 
     INTO enums, names 
     FROM employees SAMPLE (20);

print_results();
END;
/

Коллекции инициализируются автоматически. Вложенные таблицы и ассоциативные массивы расширяются для сохранения необходимого количества элементов. Если Вы используете массивы с фиксированным размером, убедитесь, что декларируемый размер массива соответствует объемам выбираемых данных. Элементы вставляются в коллекции, начиная с индекса 1, при этом все существующие значения перезаписываются.

Т.к. обработка выражения BULK COLLECT INTO подобна циклу FETCH, не генерируется исключение NO_DATA_FOUND, если не выбран ни один ряд. Если требуется, наличие выбранных данных надо проверять вручную.

Чтобы предотвратить переполнение памяти данными выборки, Вы можете использовать выражение LIMIT или псевдоколонку ROWNUM для ограничения числа записей в выборке. Кроме того возможно использование выражения SAMPLE для получения набора случайных записей.

Пример



DECLARE
  TYPE SalList IS TABLE OF employees.salary%TYPE;
  sals SalList;
BEGIN
-- Ограничение числа выбираемых записей до 50 
SELECT salary BULK COLLECT 
     INTO sals 
     FROM employees
   WHERE ROWNUM <= 50;

-- Получение 10% (приблизительно) записей  в таблице
  SELECT salary BULK COLLECT 
      INTO sals 
      FROM employees SAMPLE (10);
END;
/

Вы можете обрабатывать большие объемы результирующих данных, указав количество записей, которые будут выбраны из курсора за один раз.

Пример



DECLARE
  TYPE NameList IS TABLE OF employees.last_name%TYPE;
  TYPE SalList    IS TABLE OF employees.salary%TYPE;
  CURSOR c1 IS 
    SELECT last_name, salary 
         FROM employees 
      WHERE salary > 10000;
  names NameList;
  sals   SalList;
  TYPE RecList IS TABLE OF c1%ROWTYPE;
  recs RecList;
  v_limit PLS_INTEGER := 10;

  PROCEDURE print_results IS
  BEGIN
    IF names IS NULL OR names.COUNT = 0 THEN -- проверка, не пустая ли коллекция
      DBMS_OUTPUT.PUT_LINE('No results!');
    ELSE
      DBMS_OUTPUT.PUT_LINE('Results: ');
      FOR i IN names.FIRST..names.LAST
      LOOP
        DBMS_OUTPUT.PUT_LINE(' Employee ' || names(i) || ': $' || sals(i));
      END LOOP;
    END IF;
END;

BEGIN
  DBMS_OUTPUT.PUT_LINE('--- Обрабатываем все результаты за раз ---');
  OPEN c1;
  FETCH c1 BULK COLLECT INTO names, sals;
  CLOSE c1;
  print_results();
  DBMS_OUTPUT.PUT_LINE('--- Обрабатываем ' || v_limit || ' рядов за раз ---');
  OPEN c1;
  LOOP
    FETCH c1 BULK COLLECT INTO names, sals LIMIT v_limit;
    EXIT WHEN names.COUNT = 0;
    print_results();
  END LOOP;
  CLOSE c1;
  DBMS_OUTPUT.PUT_LINE('--- Извлекаем ряды вместо отдельных колонок ---');
  OPEN c1;
  FETCH c1 BULK COLLECT INTO recs;
  FOR i IN recs.FIRST..recs.LAST
  LOOP
-- Сейчас все колонки берем сразу из результирующего набора данных 
    DBMS_OUTPUT.PUT_LINE(' Employee ' || recs(i).last_name || ': $'|| recs(i).salary);
  END LOOP;
END;
/

Продолжение следует...


* * *

Ведущий рассылки: Петрелевич Сергей

У Вас есть вопрос? Спрашивайте
Напишите, что Вы хотите видеть в рассылке. Мне важно знать Ваше мнение.


В избранное