Меню

Консоль запросов для SAP ERP. Динамически генерируемый экран

В своей статье «Консоль запросов для SAP ERP. Выполнение SQL-запросов» я рассказывал о разработанном инструменте, который позволяет выполнять произвольные Open SQL-запросы SELECT в системах тестирования и продуктивной эксплуатации. В конце статьи я обещал рассказать об ошибках программы, с которыми пришлось столкнуться, а также о том, как можно доработать этот инструмент и в качестве чего его можно использовать.

Устраняем ошибки

В процессе работы с инструментом «вылезла» одна очень интересная ошибка: дамп GENERATE_SUBPOOL_DIR_FULL, который возникал, если, не закрывая программу, выполнить в ней 19 раз запрос (ввести запрос -> нажать «Выполнить» -> просмотреть результат -> нажать «Назад» -> ввести новый запрос -> …. ->….). Восемнадцать раз запрос отрабатывается корректно, а на девятнадцатый программа вылетает в дамп.

После изучения документации стало понятно, что существует пул временных подпрограмм, который ограничен 36 подпрограммами. Каждый вызов конструкции generate subroutine pool генерирует подпрограмму, которая помещается в этот пул. В процессе выполнения нашей программы конструкция generate subroutine pool вызывается дважды: первый раз явно в функции exec, второй раз неявно в методе cl_alv_table_create=>create_dynamic_table, вызываемом в функции create_itab_dynamically. Поэтому и получается, что за 18 раз мы полностью заполняем 36 ячеек в пуле для подпрограмм. Этот пул очищается автоматически при закрытии основной программы, вызывающей конструкцию generate subroutine pool. Метод принудительной очистки пула так и не был найден. Может быть, кто-нибудь из читателей знает и поделится знанием?

Все решения по обходу этого ограничения были, по сути своей, «костыльными» и крутились вокруг того, чтобы generate subroutine pool вынести в отдельную программу, которую следовало вызывать из основной программы через SUBMIT. Мне не особо хотелось переписывать таким образом свою программу, поэтому я пошел несколько другим путем. Был выбран, возможно, не самый корректный и идеологически не самый правильный способ обхода, но, по крайней мере, он не требовал существенной переработки логики программы: отлавливаем с помощью конструкции TRY…CATCH исключение GENERATE_SUBPOOL_DIR_FULL и, в случае его возникновения, перезапускаем текущую программу, сохранив предварительно введенный запрос.

Код программы в этом случае изменится следующим образом (На Рис 1 указаны отдельные места, где вносились изменения; добавленный код отмечен зеленым цветом; полный листинг программы можно посмотреть в предыдущей статье):

=================

  …

  …

  …

form exec.

  …

  …

  …

  delete lt_sql_query where line = ''.

  " сохраняем введенный запрос в ABAP Memory

  export lt_sql_query from lt_sql_query to memory id 'ZSQL'.

  …

  …

  …

  try.

      generate subroutine pool code name prog

                                    message msg.

    catch generate_subpool_dir_full.

      submit zsql.

  endtry.

  …

  …

  …

endform. 

  …

  …

  …

module pbo output.

  …

  …

  …

  " Выводим на форму сохраненный запрос

  "

  data lt_saved_sql_query type ty_simple_tab.

  import lt_sql_query to lt_saved_sql_query from memory id 'ZSQL'.

  call method g_editor->set_text_as_r3table

    exporting

      table = lt_saved_sql_query.

endmodule.

  …

  …

  …

=================

Рис.1. Изменения кода программы

Развитие инструмента. Динамически генерируемый экран

Первая версия программы была тщательно протестирована коллегами в боевых условиях. Вердикт: в текущем виде инструмент непригоден для эксплуатации. С помощью него можно отрабатывать только те запросы, которые не содержат много фильтрующих значений в условиях выборки. Ну, то есть, запрос SELECT * FROM bseg WHERE belnr = ‘123456’ OR belnr = ‘654321’ отработать можно, а вот если в качестве параметров для поля belnr нужно передать несколько тысяч значений, то сделать это уже будет проблематично (а если точнее, то нереально).

«Эх, было бы круто, если бы после ввода запроса выходило окно, куда можно было бы ввести неограниченное число параметров выборки», - говорили коллеги. Для себя я сформулировал задачу так: инструмент должен позволять указывать в качестве параметров выборки сложные условия, которые могут содержать многотысячные параметры отбора. Для этого нужно перед выполнением запроса отображать модальное окно, в котором эти параметры можно ввести привычным для SAP способом. Причем, перечень полей в этом окне должен меняться в зависимости от введенного запроса.

Первое, что нужно было сделать, - это как-то дать понять программе, что для определенных запросов нужно выводить модальное окно, куда можно ввести параметры. Для этого я ввел несложное правило, которое нужно соблюдать при написании запроса: если нужно для какого-либо поля передать сложное условие выборки, то перед названием этого поля в WHERE нужно поставить &&. В этом случае, программа будет понимать, что для такого поля нужно показать модальное окно, куда будут введены значения параметров. Забегая немного вперед, покажу на примере. Для запроса SELECT * FROM bseg WHERE bseg~gjahr = ‘2013’ AND &&bseg~belnr AND &&bseg~bukrs будет выведено модальное окно с двумя полями с возможностью массового ввода (Рис. 2).

Рис. 2. Модальное окно для ввода значений параметров

Дальше нужно было разобраться с генерацией динамических экранов. После изучения нескольких различных функциональных модулей и классов выбор пал на группу функциональных модулей  FREE_SELECTIONS_*, как наиболее подходящих для нашей задачи. Из этой группы нас интересовали:

  1. Функциональный модуль FREE_SELECTIONS_DIALOG, который на входе получает названия таблиц и полей, а на выходе выдает сгенерированный экран, на котором эти поля отображаются.
  2. Функциональный модуль FREE_SELECTIONS_RANGE_2_WHERE, который преобразует введенные в модальном окне значения в формат WHERE для SQL-запроса (Рис 3.).

Рис 3. Преобразование множественного выбора в WHERE условие

Дело осталось за малым: реализовать это всё в коде. Укрупненно, нужно сделать следующее: 

  1. Доработать функцию parse_sql_query, добавив в нее функциональность определения конструкций вида &&bseg~belnr.
  2. Если такие конструкции встречаются, то вывести для них сгенерированное модальное окно.
  3. В функции create_get_function заменить в запросе конструкции вида &&bseg~belnr реальным условием выборки.

Вот как это выглядит в коде (указаны отдельные места, где вносились изменения; на Рис.4. добавленный код отмечен зеленым цветом; полный листинг программы можно посмотреть в предыдущей статье):

=================

  …

  …

  …

types: begin of ty_multiple_selection,

              line(255) type c,

              tab_name(30) type c,

              field_name(30) type c,

              where_condition type rsds_where_tab,

           end of ty_multiple_selection.

  …

  …

  …

form exec.

  …

  …

  …

  " Парсим запрос и получаем названия выбираемых полей и таблиц

  data: lt_fields type ty_simple_tab,

            lt_tables type ty_simple_tab,

            lt_multiple_selection type ty_multiple_selection_tab,

            l_use_cnt(1) type c.

  clear: lt_fields, lt_tables, lt_multiple_selection, l_use_cnt.

  perform parse_sql_query

          using

            lt_sql_query

          changing

            lt_fields

            lt_tables

            lt_multiple_selection

            l_use_cnt.

  " Запрашиваем параметры для полей, требующих множественный ввод

  "

  if lines( lt_multiple_selection ) > 0.

    perform show_dynamic_selection

            changing

              lt_multiple_selection.

  endif.

  …

  …

  …

  perform create_get_function

          using

            lt_sql_query

            lt_multiple_selection

          changing

            code.

  …

  …

  …

endform.

  …

  …

  …

" Функция разбора запроса

form parse_sql_query

     using

       lt_sql_query type ty_simple_tab

     changing

       lt_fields type ty_simple_tab

       lt_tables type ty_simple_tab

       lt_multiple_selection type ty_multiple_selection_tab

       l_use_cnt. 

  …

  …

  …

      " Получаем названия таблиц   

      if l_is_tabname = 'X'.

        append <fs_parsed_sql_line>-line to lt_tables.

        clear l_is_tabname.

      endif.

      " Получаем поля, которые требуют массового ввода параметров

      "

     

Если хотите прочитать статью полностью и оставить свои комментарии присоединяйтесь к sapland

У вас уже есть учетная запись?

Войти

Обсуждения Количество комментариев1

Комментарий от  

Олег Точенюк

  |  18 мая 2016, 13:47

Ну что вам сказать, лучшие собак(SAP)оводы не рекомендуют использовать cl_alv_table_create=>create_dynamic_table, по причине переполнения именно этого самого буфера. Вместо этого предлагается использовать отдельно динамическое создание таблицы, используя технику RTTS (про это читаем справку). Для примера как сделать вывод такой динамически созданной таблицы можно сходить покурить код сюда: zevolving.com/2008/09 , ну это если хотите допилить текст до полной кошерности.