Утилита MSLOV была написана в августе 2001 года.
Мне кажется она имеет право на существование.
Похожая реализация (Multi Select T-List) на
OTN
использует PJC (Pluggable Java Components) и, соответственно,
будет работать далеко не на всех версиях и конфигурациях Forms.
Описание утилиты построено в виде вопросов-ответов.
Разработчикам Oracle Forms хорошо известны такие объекты форм как списки значений (LOV). С их помощью можно легко и быстро реализовать выбор нужной записи из динамически построенного списка. Список этот как правило основывается на команде SELECT, хотя может быть построен и "вручную".
Однако бывают случаи, когда нужно выбрать не одну, а сразу несколько записей из списка значений. Например, в форме управления заказами (смотри форму ORD_DEMO), построенной на до боли знакомых таблицах ORD и ITEM, было бы неплохо для заполнения детального блока (ITEM) вызвать список значений, построенный на таблице PRODUCT, и дать пользователю возможность выбрать сразу несколько товаров одновременно. И затем, для каждого выбранного товара создать запись в блоке ITEM. К сожалению, возможности встроенных списков значений не позволяют это сделать. И если Вы все-таки намерены реализовать такую возможность, то придется немало потрудиться.
Но что Вы скажете если всей этой функциональности можно добиться при помощи следующего триггера KEY-LISTVAL:
IF fp_mslov.show_lov ( label_in => 'descrip', id_in => 'prodid', from_clause_in => 'FROM product ORDER BY descrip', col1_in => 'descrip', title_in => 'Select Products' ) THEN fp_mslov.add_selected_records ('ITEM.prodid', 'ITEM.descrip'); END IF;
Если Вам нравится, то эта статья для Вас.
Итак, речь пойдет об утилите, предназначенной для реализации списков, предоставляющих пользователю возможность выбора нескольких записей одновременно.
Несмотря на то, что разработка велась на Oracle Forms 6i, я думаю что утилита должна работать и на более ранних версиях. По крайней мере в ней не используется ничего, что не могло бы работать в Oracle Forms 5.0. И совершенно точно она будет работать на Forms9i. Для этого требуется лишь перекомпиляция модулей. Для работы в web (да и в клиент-сервер) вероятно потребуется изменить размеры, цвета, шрифты, но на вкус и цвет... сами понимаете, компромисс найти сложно.
Этот раздел предназначен для тех кто уже не новичек в Forms.
Если же Вы недавно начали работать с Forms или не хотите вдаваться
в подробности реализации, а хотите просто использовать MSLOV,
то Вам нужно сюда.
Если же Вы считаете себя специалистом по Forms, но все равно в
этом разделе что-то не понятно, то вероятно я плохо сумел это
объяснить. Попробуйте задать вопрос.
С точки зрения интерфейса пользователя, утилита должна
выводить на экран окошко с полями, кнопками и т.д.
Oracle Forms предлагает два способа создания такого рода
компонент:
Я предпочитаю второй способ. Во-первых, мне пришлось очень много и долго работать с Forms 3.0, где другого способа и не было. Во-вторых, использование отдельного модуля формы имеет то преимущество, что в случае изменений в нём, достаточно просто заменить старую версию формы на новую. Если же использовать Группу Объектов, то при любом изменении членов Группы потребуется перекомпиляция всех форм, на эту Группу ссылающихся.
Теперь, с точки зрения того, что пользователь должен увидеть на экране и как он должен сделать выбор. Опять же, есть два основных способа реализовать выбор нескольких значений:
Опять же, я предпочёл второй вариант, хотя никаких его особых преимуществ не вижу. Точно также можно было бы реализовать и первый. А самое лучшее решение, которое возможно будет реализовано, это сделать поддержку обоих стилей и тогда разработчик сам сможет выбрать то, что ему больше нравится.
Итак, с внешним видом определились, это отдельный модуль формы (MSLOV.fmb). Со стилем тоже определились. Теперь нужно определиться как эта форма узнает что предложить на выбор пользователю(чем заполнить левый список) и как/куда вернуть сделанный им выбор(правый список).
В подавляющем большинстве случаев, LOV-ы строятся на результатах запроса. Значит было бы не плохо передавать в форму параметром команду SELECT и пусть эта форма сама этот запрос и выполняет. Т.е. нам нужно чтобы наш левый список представлял из себя отдельный блок, причем связанный с базой данных(Database Data Block=Yes), да так чтобы выполнение EXECUTE_QUERY для этого блока выполняло переданный параметром в форму запрос(по сути произвольный запрос).
Почему это было бы не плохо? Да потому, что выполнение запроса в блоке имеет важное преимущество перед стандартными LOV-ами или заполнением блока "вручную". Мы по умолчанию начинаем использовать буфферизированную выборку данных на клиента(свойство блока Number of Records Buffered). Такой подход позволит легко реализовать выбор из запросов, возвращающих очень много записей. Записи будут подгружаться на клиента только по мере необходимости, а не все сразу. Стандартный LOV, в свою очередь, всегда "вытаскивает" на клиента все записи запроса.
Вопрос в том, как добиться выполнения в блоке произвольного запроса. Мы можем, конечно, поставить свойство блока 'Query Data Source Type' в значение 'FROM clause query' и можем программно менять свойство блока 'Query Data Source Name', но Forms не позволяет программно добавлять новые элементы в блок и менять названия уже существующим. Поэтому для реализации задуманного требуется наложить ряд ограничений на "произвольность" запроса. Точнее, это ограничения на количество и названия столбцов в списке SELECT. Мы должны заранее знать сколько их и как они называются. В предложенной реализации столбцов восемь:
Ну а окончательный вид запроса, выполняемого в блоке, будет выглядеть так:
'(SELECT rownum, MSLOV.* FROM (' || sql_in || ') MSLOV)'
Где sql_in - переданный в форму параметром запрос.
Т.о. столбец ROWNUM мы формируем сами, а остальные столбцы
должны правильно быть сформированы перед вызовом формы.
При этом LABEL и ID - столбцы обязательные, без них вообще
нет смысла вызывать форму, а COLX - необязательные.
Остальная часть команды SELECT, начиная с FROM может формироваться
пользователем как угодно.
С возвращаемым набором записей - проще. Мы можем использовать глобальные группы записей(группы созданные командой 'CREATE_GROUP (group_name, GLOBAL_SCOPE);'). Такие группы, как и глобальные переменные действуют на протяжении всего сеанса Forms Runtime и могут использоваться для обмена группами записей между формами.
Для того чтобы разработчику не ошибиться при формировании запроса и для того чтобы не вдаваться в тонкости работы с группами записей нужно создать удобное API для работы с формой и возвращаемым значением. Этим API стал пакет FP_MSLOV из PL/SQL библиотеки MSLOV.pll
Впервую очередь нужно присоединить библиотеку MSLOV.pll к Вашей форме и обеспечить доступ к MSLOV.fmx. Т.е. эта форма должна находится либо в текущем каталоге, либо в каталоге из списка FORMS60_PATH. (ВНИМАНИЕ. Форма MSLOV предлагается в исходных кодах, в виде FMB файла, поэтому перед использованием скомпилируйте исполняемый FMX файл)
Теперь Вы готовы к использованию MSLOV.
Первой вызывается функция SHOW_LOV. Это основная подпрограмма пакета,
которая собственно и вызывает форму выбора. Название функции и ее возвращаемое
значение в точности соответствуют встроенной функции SHOW_LOV.
Т.е. FP_MSLOV.show_lov вернет Истину если пользователь сделал выбор и Ложь
в противном случае.
Для вызова функции Show_Lov необходимо сформировать список входных параметров. Функция имеет большое количество параметров, многие из которых имеют значения по умолчанию и при вызове могут не указываться. Поэтому предпочтительной формой передачи параметров является передача параметров по имени (param => value).
Для того чтобы лучше понять назначение отдельных параметров,
рассмотрим принцип работы утилиты.
Список возможных значений для выбора строится на основе команды SELECT,
на формирование которой налагается ряд ограничений.
Точнее ограничения налагаются на список столбцов,
расположенных между ключевыми словами SELECT и FROM.
Этот список всегда должен начинаться со столбцов с алиасами LABEL и ID.
Это сделано по аналогии с элементами типа LIST.
Т.е. первый столбец (LABEL) будет показан пользователю на экране,
второй столбец (ID) предназначен для хранения и возврата связанного
с LABEL значения.
Т.о. в минимальном виде, запрос для построения LOV будет выглядеть так:
SELECT label_in AS LABEL, id_in AS ID FROM table_name
Для того чтобы не передавать весь текст запроса одним параметром и соответственно не писать "разборщик" запроса, в функции SHOW_LOV есть несколько параметров из которых и будет "сложена" окончательная команда SELECT. В общем виде разбивка на параметры выглядет так: по отдельному параметру на каждый столбец и один параметр на 'FROM...' и всё остальное.
Теперь можно перейти к описанию параметров функции SHOW_LOV
Параметр | Описание |
---|---|
LABEL_IN | Столбец или выражение, которое будет использовано как LABEL для отображения на экране. Алиас столбца 'AS LABEL' добавлять не нужно, т.к. он будет подставлен автоматически. |
ID_IN | Столбец или выражение, которое будет использовано в качестве возвращаемого значения. Алиас столбца 'AS ID' не нужен. |
FROM_CLAUSE_IN | Часть команды SELECT начиная с фразы FROM. Должна начинаться с ключевого слова 'FROM' и может включать необязательные фразы WHERE, GROUP BY, ORDER BY (начиная с 8i), START WITH .. CONNECT BY и т.д. |
COL1_IN COL2_IN COL3_IN COL4_IN COL5_IN |
При работе со списками значений часто бывает необходимо
вернуть для выбранной записи более одного столбца.
Для реализации такой потребности в функции Show_Lov
можно определить помимо ID еще до пяти возвращаемых столбцов.
Для этого служат параметры col1_in, col2_in,..., col5_in. В этих необязательных параметрах могут быть описаны дополнительные столбцы или выражения. |
TITLE_IN | Определяет заголовок окна выбора значений |
Итак, если Show_Lov вернула Истину, то это значит, что во внутренних структурах (глобальная группа записей) сформировался массив выбранных значений.
Для получения доступа к этим значениям предназначены следующие подпрограммы:
Подпрограмма | Описание |
---|---|
ROWCOUNT | Функция, возвращает количество выбранных записей |
GET_ROW | Функция, возвращает информацию об одной выбранной записи, по указанному параметром номеру. Допустимые значения для параметра от 1 до ROWCOUNT |
CLEAR | Производит очистку памяти, отведенной под хранение выбранных значений |
ADD_SELECTED_RECORDS | Для каждой записи выбранной пользователем, создает в текущем блоке новую запись и заполняет указанные параметрами поля. По окончании, освобождает память. |
Примеры
Сценарий 1.
В форме управления сотрудниками (EMP_DEMO) мы хотим
предоставить пользователю возможность поиска сотрудников
в заданных отделах. Например, показать всех сотрудников 10 и 20
отделов. "Продвинутый" пользователь, конечно, может в режиме
ввода запроса ввести '# IN (10, 20)' в поле DEPTNO блока EMP,
но для этого нужно знать не только правила построения такого
запроса, но и номера отделов.
Мы бы хотели чтобы пользователь вызвал список значений, в котором перечислены названия(DNAME) и месторасположения отделов(LOC), выбрал бы нужные отделы, а мы бы уже сформировали значение поле DEPTNO на основе сделанного выбора.
Нужная нам команда SELECT выглядет так:
SELECT dname ||' (' || loc || ')' AS LABEL, deptno AS ID FROM dept ORDER BY dname
Ну а примерный вид триггера KEY-LISTVAL, вызывающего MSLOV в режиме ввода запроса, будет таким:
DECLARE -- Тип FP_MSLOV.OUT_RECORD_TYPE является записью, -- именно эту запись и возвращает функция GET_ROW. row_v fp_mslov.out_record_type; -- Здесь будет формироваться значение поля 'EMP.DEPTNO' deptno_v VARCHAR2(2000); BEGIN -- Вызываем MSLOV только в режиме ввода запроса IF :SYSTEM.MODE = 'ENTER-QUERY' THEN -- Вот в такой вызов MSLOV трансформировался вышеприведенный запрос IF fp_mslov.show_lov ( label_in => 'dname ||'' (''||loc||'')''', id_in => 'deptno', from_clause_in => 'FROM dept ORDER BY dname', title_in => 'Select Departments' ) THEN deptno_v := '# IN ('; -- Для каждой выбранной записи FOR i IN 1 .. fp_mslov.rowcount LOOP -- Получаем очередную запись row_v := fp_mslov.get_row (i); -- Считываем значение поля ID deptno_v := deptno_v || row_v.id; IF i < fp_mslov.rowcount THEN deptno_v := deptno_v || ','; END IF; END LOOP; deptno_v := deptno_v || ')'; -- Освобождаем память, связанную с MSLOV fp_mslov.clear; COPY(deptno_v, 'EMP.deptno'); END IF; END IF; END;
Сценарий 2.
В форме ввода заказов (ORD_DEMO) мы хотим упростить пользователю
заполнение детального блока(ITEM) с информацией о заказываемых товаров.
Пользователь должен иметь возможность вызвать список имеющихся товаров,
выбрать нужные и в соответствии с этим выбором, в блоке ITEM должны
появиться новые записи о каждом выбранном товаре. Причем из списка
значений нам нужно получить не только код товара(PRODID), но и
описание(DESCRIP) и стандартную цену из таблицы PRICE.
Примерный вид PL/SQL кода, реализующего это с использованием MSLOV:
DECLARE from_clause_v VARCHAR2(32000) := 'FROM product a, price b WHERE a.prodid = b.prodid AND (b.prodid, NVL(b.enddate, TO_DATE(''9999'',''YYYY''))) IN (SELECT prodid, MAX(NVL(enddate, TO_DATE(''9999'',''YYYY''))) FROM price GROUP BY prodid) ORDER BY a.descrip'; BEGIN IF fp_mslov.show_lov ( label_in => 'a.descrip', id_in => 'a.prodid', from_clause_in => from_clause_v, col1_in => 'a.descrip', col2_in => 'b.stdprice', title_in => 'Select Products' ) THEN -- Для каждого выбранного товара, будет создана запись -- в блоке ITEM. -- Выбранное значение ID будет присвоено в 'ITEM.prodid' -- Выбранное значение COL1_IN будет присвоено в 'ITEM.descrip' -- Выбранное значение COL2_IN будет присвоено в 'ITEM.actualprice' fp_mslov.add_selected_records ( 'ITEM.prodid', 'ITEM.descrip', 'ITEM.actualprice' ); END IF; END;
Важное замечание относительно демо форм.
Приведенные формы ORD_DEMO и EMP_DEMO имеют очень существенный минус
в реализации. В них не используются связанные(bind) переменные.
Например, в форме EMP_DEMO значение поля DEPTNO формируется в виде
'# IN (10,40)'. Т.е. с жестко зашитыми номерами отделов.
Неиспользование связанных переменных существенно
сказывается на произволительности приложения, но эта тема
требует отдельного разговора и, самое главное, дополнительного
программирования, которое может "заслонить" собой использование MSLOV.
Утилита состоит из нескольких файлов, лежащих в архиве
mslov.zip
Ниже дано описание каждого из них:
Файл | Описание |
---|---|
DEMOBLD.SQL | Демонстрационные формы, описанные ниже, требуют для своей работы стандартные оракловские демо таблицы: DEPT, EMP, ORD, ITEM, PRODUCT. Если у вас нет этих таблиц, то можете использовать этот скрипт для их создания. |
ORD_DEMO.fmb | Демонстрационная форма ввода заказов. Стоя на любом заказе, нажмите на кнопку 'Select Products'. Выбранные товары будут занесены в детальный блок формы. |
EMP_DEMO.fmb | Демонстрационная форма управления сотрудниками. MSLOV используется при вводе запроса, для выбора нескольких отделов одновременно. Следуйте краткой инструкции на экране. Она хоть и на английском, но думаю будет понятна всем. |
MSLOV.fmb | Это именно та форма, которую Вы видите при вызове MSLOV |
MSLOV.pll | Это именно та PL/SQL библиотека, в которой находится пакет FP_MSLOV |
Если у Вас появился вопрос, по использованию MSLOV или Вы обнаружили ошибки, или хотите предложить улучшения/дополнения, да в конце концов, просто хотите что-то сказать, то пишите мне или оставте сообщение в гостевой книге
22 августа 2002г.