Как задать и использовать константу в виде коллекции на Pl/SQL.

Иногда в реальных задачах необходимо в константах хранить какую-либо коллекцию. На языке pl/sql просто создать коллекцию и перечислить через запятую данные, которые она будет содержать, нельзя. Такое возможно только с массивами, у которых строго задан размер и тип элементов. Если же возникла необходимость сделать предопределенную коллекцию-константу, то в этом случае её не получится проинициализировать простым перечислением данных. Чтобы проинициализировать коллекцию–константу, необходимо задать функцию, которая вернет заполненную коллекцию для присвоения её в указанную переменную. Выглядеть это будет примерно так, как показано ниже.

Например, мы хотим создать коллекцию действий пользователя, индекс элемента которой будет представлять собой символьный код операции, а значение — название операции, которое увидит пользователь. Зададим возможные действия константами, так как в дальнейшем в программе они обрабатываются.

subtype typeActionName is varchar2(50);                      -- Тип: Код действия
type typeActionCol is table of typeActionName                -- Тип: Список действий   
  index by varchar2(200);

cOperSetDefault       constant typeOperName:= 'SetDefault’;        
cOperUndoDefault      constant typeOperName:= 'UndoSetDefault';       
cOperChangeParam      constant typeOperName:= 'ChangeParam';      
cOperClose            constant typeOperName:= 'CLOSE';          
cOperOpen             constant typeOperName:= 'UNCLOSE';        
cOperDelete           constant typeOperName:= 'Delete';              
cOperAddItem          constant typeOperName:= 'AddItem';          

function setActionCollection  return typeOperCol;            --функция задания массива кода операции и имени операции

ActionCollection constant typeOperCol:= setActionCollection;  --список действий пользователя

function setActionCollection return typeActionCol
  is
    pOperCol  typeActionCol;
  begin
    pOperCol(cActionSetDefault)    := 'Установить параметры по умолчанию'; 
    pOperCol(cActionUndoDefault)   := 'Вернуть параметры, которые были до сброса';    
    pOperCol(cActionCloseAgree)    := 'Закрыть соглашение';
    pOperCol(cActionOpenAgree)     := 'Открыть соглашение';
    pOperCol(cActionChangeParam)   := 'Изменить значение параметров;
    pOperCol(cActionDelete)        := 'Удалить элемент';      
    pOperCol(cActionAddItem)       := 'Добавить элемент';  
    return  pOperCol;                             
  end;

Таким образом можно задать коллекцию, содержащую данные любого составного типа. В зависимости от типа будет меняться только функция его заполнения (либо усложнятmся, либо упрощаться). После того, как мы объявили свою константу, возникает вопрос: как выполнить какие-либо операции при помощи данных, которые мы проинициализировали? Если рассматривать коллекцию, которую я привела выше, то для обхода её потребуется массив индексов.

type typeColkeys is varray(7) of typeOperName;

--массив для индексации действий
    Collectionkeys typeColkeys  := typeColkeys (cActionSetDefault, cActionUndoDefault, cActionCloseAgree, cActionOpenAgree, cActionChangeParam, cActionDelete, cActionAddItem);

А после этого можно будет пройти по коллекции циклом и выполнить необходимые вам операции.

    FOR i IN 1..ActionCollection.count loop
        --выполняем необходимые операции
        --Collectionkeys(i) – это будет код действия пользователя 
        --ActionCollection(Collectionkeys(i)) –это будет заголовок действия 
    end loop;  

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

Конструкция select for update

Ни для кого не секрет, что в различных СУБД блокирование реализовано по разному. Есть несколько основных принципов блокирования в СУБД Oracle, незнание которых может привести к возникновению ошибок в приложениях.

  • Oracle блокирует данные на уровне строк и только при изменении. Эскалация блокировок до уровня блока или таблицы никогда не выполняется.
  • Oracle никогда не блокирует данные с целью считывания. При обычном считывание блокировки на строки не устанавливаются.
  • Сеанс, записывающий данные не блокирует сеанс, читающий данные.
  • Сеанс записи данных блокируется, только если сеанс записи уже заблокировал строку, которую предполагается изменять. Сеанс считывания данных никогда не блокирует сеанс записи.

Читать далее

Oracle APEX: Итоги в Interactive report на каждой странице [RU]

Одним из самых мощных инструментов визуализации данных в Oracle APEX является Interactive Report. Он предоставляет богатые возможности поиска, фильтрации, сортировки и вычисления итогов. Но есть ряд задач, часто возникающих в разработке бизнес-приложений, которые вызывают сложности. В нескольких постах я хочу предложить простой выход из положения.

Первым рассмотрим тривиальный вопрос — вывод итогов по всем данным на каждой странице отчета. При использовании встроенных итогов в IR, итоги выводятся на самой последней странице, если страниц несколько. В реальных приложениях очень часто итоги как раз и будут целью фильтрации и отображения данных — пользователю нужно сначала увидеть общую сумму, а потом уже, если она устраивает, смотреть детализацию. Если после применения фильтра пользователю приходится «отмотать» сначала 10-20 страничек, либо грузить ооочень длинную таблицу — пользователь звереет.

Оптимальным выходом является отображение итогов на каждой странице, но IR не предоставляет такой возможности. Значит, сделаем это сами.

In english, please…

Читать далее

Как создать индекс в Oracle, если таблица занята.

Иногда бывают такие ситуации, когда что-то случилось с индексами на важной таблице. Если работа с БД непрерывная и эта таблица очень часто занята, восстановить индексы достаточно проблематично, так как ресурс постоянно занят. Конечно можно поймать такой момент когда СУБД даст возможность создать index. Однако если эта проблема всплыла только после запуска длительной и важной операции, которая не требует отлагательств. Так как объект занят вам придется писать код создания индекса и сидеть давить, ловя момент когда пройдет commit и объект разблокируется. Не очень радужные перспективы. По-этому умные люди в Oracle подумали об этом. В Oracle Enterprise Edition есть возможно запустить создание индекса в режиме ожидания. К сожалению уже в версии Standard Edition данная возможность отсутствует. Что бы запустить создание индекса, когда ресурс занят необходимо дописать в конце ONLINE.

CREATE INDEX index_name ON table_name (column_name, column_name, column_name, column_name) ONLINE;

Операция запуститься и будет ждать момента, когда ресурс освободится. Как только пройдет commit запустится создание индекса. Имейте в виду, что время, необходимое для завершения построения онлайн индекса пропорциональный размеру таблицы и числу одновременно выполняющихся DML отчетности. Поэтому, лучше всего, начинать оперативное построение индекса при низкой DML активности.

Очень полезная feature. Если у вас версия Enterprise Edition советую пользоваться, так как она нужна не только в ситуации, которую описала я. Почитать документацию об этом можно здесь.