Изучая исходники, наткнулся на весьма полезный класс для извлечения метаданных Hana модели. Не нашел ни одной статьи где бы он упоминался.
Описание класса на sapdatasheet
Как пользоваться
Для начала создадим Calculation view типа cube, добавим в него агрегированную колонку и входящий параметр. В качестве входящих данных я взял таблицу SFLIGHT.

Фильтр наложил на поле CURRENCY, по полю PAYMENTSUM сделал агрегацию типа SUM.
Генерируем SQL запрос.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
SELECT "CARRID", "CONNID", "FLDATE", "PRICE", "CURRENCY_1", "PLANETYPE", "SEATSMAX", "SEATSOCC", "SEATSMAX_B", "SEATSOCC_B", "SEATSMAX_F", "SEATSOCC_F", sum("PAYMENTSUM") AS "PAYMENTSUM" FROM "_SYS_BIC"."ocrv.rgp.STADNIKOV/EXAMPLE"('PLACEHOLDER' = ('$$CURRENCY$$', '<Enter Value>')) GROUP BY "CARRID", "CONNID", "FLDATE", "PRICE", "CURRENCY_1", "PLANETYPE", "SEATSMAX", "SEATSOCC", "SEATSMAX_B", "SEATSOCC_B", "SEATSMAX_F", "SEATSOCC_F" |
Итак, на этом этапе нам важно извлечь из запроса 2 параметра.
- Путь ( ocrv.rgp.STADNIKOV );
- имя модели ( EXAMPLE )
Пишем код на ABAP, применяем класс cl_rsdd_hana_model
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
data: e_t_metadata type cl_rsdd_hana_model=>tn_t_metadata, e_s_cube type cl_rsdd_hana_model=>tn_s_cube, e_t_var type cl_rsdd_hana_model=>tn_t_var. data: hana_model type ref to cl_rsdd_hana_model. try. hana_model = cl_rsdd_hana_model=>factory( i_catalog_name = 'ocrv.rgp.STADNIKOV' i_cube_name = 'EXAMPLE' ). catch cx_root. exit. endtry. hana_model->get_metadata( importing e_t_metadata = e_t_metadata e_s_cube = e_s_cube e_t_var = e_t_var ). |
Какие данные можно получить
В итоге я получил 2 внутренних таблицы и структуру.
e_t_metadata — каталог полей со всеми свойствами

Тут можно найти: названия полей, их описания, лейблы, типы данных в SQL и ABAP форматах, размеры целой и десятичной частей, агрегированные поля с названием агрегирующей функции. Более подробно об структуре, можно узнать по ссылке из начала статьи в графе Types.
e_t_var — входящие параметры модели.

Для нашей модели определился входящий параметр currency. В этой внутренней таблице можно найти: название, ABAP тип, размер и другие. Как вариант практического использования с помощью get_metadata() можно проверить какие у модели есть входящие параметры во время исполнения (at runtime).
Пожалуй самое интересное находится в структуре E_S_CUBE.

Мы тут видим все свойства Calculation view. Директорию, имя, тип, схему, описание, автора и даже даты создания и активации.
Что с этим можно сделать
Например можно динамически построить полноценный field catalog, с его помощью создавать внутреннюю таблицу и затем вызывать Hana модель, т.е. без предварительного объявления структуры для внутренней таблицы. Кратко об этом, я рассказывал в прошлой статье. Там я показал как вызвать соответствующий метод глобального класса. (Код класса)
Итак, еще раз, вызов. SQL запрос из примера выше, CURRENCY = EUR.
1 2 3 4 5 6 7 8 9 10 11 |
data: data_tab type ref to data, sql type string. sql = |select "CARRID", "CONNID", "FLDATE", "PRICE", "CURRENCY_1", "PLANETYPE", "SEATSMAX", | && | "SEATSOCC", "SEATSMAX_B", "SEATSOCC_B", "SEATSMAX_F", "SEATSOCC_F", sum("PAYMENTSUM") AS "PAYMENTSUM" | && | from "_SYS_BIC"."ocrv.rgp.STADNIKOV/EXAMPLE"('PLACEHOLDER' = ('$$CURRENCY$$', 'EUR')) | && | group by "CARRID", "CONNID", "FLDATE", "PRICE", "CURRENCY_1", "PLANETYPE", "SEATSMAX", "SEATSOCC", "SEATSMAX_B", "SEATSOCC_B", "SEATSMAX_F", "SEATSOCC_F"|. data_tab = zcl_sql_executor=>result_hana_model( exporting i_query = sql ). field-symbols: <result_table> type any table. assign data_tab->* to <result_table>. |
Реализация
Первым делом необходимо распарсить SQL строку запроса, извлечь из нее директорию, имя Hana модели и «хвост» SQL (вся строка после имени модели, она понадобится когда мы будем собирать запрос).
Логика этого парсинга у меня вынесена в метод get_model_data().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
* <SIGNATURE>---------------------------------------------------------------------------------------+ * | Static Private Method ZCL_SQL_EXECUTOR=>GET_MODEL_META * +-------------------------------------------------------------------------------------------------+ * | [--->] I_SQL TYPE STRING * | [<---] E_CATALOG_NAME TYPE STRING * | [<---] E_CUBE_NAME TYPE STRING * | [<---] E_REQUEST_TAIL TYPE STRING * +--------------------------------------------------------------------------------------</SIGNATURE> method GET_MODEL_META. split i_sql at '.' into data(param1) data(param2). find first occurrence of regex '\"\S+\"' in param2 match offset data(moff) match length data(mlen). param1 = param2+moff(mlen). e_request_tail = param2+mlen. translate param1 using '" '. condense param1. split param1 at '/' into e_catalog_name e_cube_name. endmethod. |
1 2 3 4 |
zcl_sql_executor=>get_model_meta( exporting i_sql = sql importing e_catalog_name = e_patch e_cube_name = e_model_name e_request_tail = data(request_tail) ). |
По итогу получаются следующие результаты
- E_PATCH = ocrv.rgp.STADNIKOV
- E_MODEL_NAME = EXAMPLE
- REQUEST_TAIL = (‘PLACEHOLDER’ = (‘$$CURRENCY$$’, ‘EUR’)) GROUP BY «CARRID», «CONNID», «FLDATE», «PRICE», «CURRENCY_1», «PLANETYPE», «SEATSMAX», «SEATSOCC», «SEATSMAX_B», «SEATSOCC_B», «SEATSMAX_F», «SEATSOCC_F»
Извлекаем метаданные
1 2 3 4 5 6 7 8 9 10 11 12 |
data: hana_model type ref to cl_rsdd_hana_model. try. hana_model = cl_rsdd_hana_model=>factory( i_catalog_name = e_patch i_cube_name = e_model_name ). catch cx_root. exit. endtry. hana_model->get_metadata( importing e_t_metadata = data(metadata) e_s_cube = data(s_cube) ). e_schema = s_cube-schema_name. sort metadata by key_column descending column_name. |
Далее необходимо сформировать блок запроса select по набору вызываемых полей и параллельно сформировать field catalog.
Тут я реализовал 2 варианта, select со звездочкой (*) и набором вызываемых полей.
Select со звездочкой (*)
В этом варианте все просто, перебираем весь набор полей, если есть агрегатные функции, то учитываем это.
1 2 3 4 5 6 7 8 9 10 11 12 |
loop at metadata into data(metadata_line). if metadata_line-aggrgen is not initial. e_sql = |{ e_sql } { metadata_line-aggrgen }("{ metadata_line-column_name }") as "{ metadata_line-column_name }"|. else. e_sql = |{ e_sql } "{ metadata_line-column_name }"|. endif. if sy-tabix <> lines( metadata ). e_sql = e_sql && ','. endif. zcl_sql_executor=>append_field( exporting i_meta_line = metadata_line changing c_catalog = e_catalog ). endloop. |
Метод append_field()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
* <SIGNATURE>---------------------------------------------------------------------------------------+ * | Static Private Method ZCL_SQL_EXECUTOR=>APPEND_FIELD * +-------------------------------------------------------------------------------------------------+ * | [--->] I_META_LINE TYPE CL_RSDD_HANA_MODEL=>TN_S_METADATA * | [<-->] C_CATALOG TYPE LVC_T_FCAT * +--------------------------------------------------------------------------------------</SIGNATURE> method APPEND_FIELD. data: fcat_property type lvc_s_fcat. fcat_property-col_pos = sy-tabix. fcat_property-fieldname = i_meta_line-column_name. fcat_property-outputlen = i_meta_line-intlen. fcat_property-datatype = i_meta_line-datatp. fcat_property-intlen = i_meta_line-intlen. fcat_property-reptext = i_meta_line-column_name. fcat_property-scrtext_l = i_meta_line-column_name. fcat_property-scrtext_m = i_meta_line-column_name. fcat_property-scrtext_s = i_meta_line-column_name. fcat_property-decimals = i_meta_line-decimals. fcat_property-just = abap_true. fcat_property-lowercase = abap_true. append fcat_property to c_catalog. endmethod. |
Select с набором вызываемых полей
Тут немного сложнее. Мы знаем что набор полей идет через запятую до ключевого слова FROM, исходя из этих правил создаем внутреннюю таблицу с набором полей из запроса, перебираем их, ищем соответствия в полученном наборе полей, формируем начало строки SQL.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
split sql at 'FROM' into data(select_feild) data(trash). select_feild = select_feild+6. condense select_feild. split select_feild at ',' into table data(field_list). loop at field_list into data(field). translate field to upper case. if field cp '*SUM(*)*' or field cp '*MIN(*)*' or field cp '*MAX(*)*'. split field at 'AS' into trash select_feild. translate select_feild using '" '. condense select_feild. metadata_line = metadata[ column_name = select_feild ]. e_sql = |{ e_sql } { field }|. else. translate field using '" '. condense field. metadata_line = metadata[ column_name = field ]. e_sql = |{ e_sql } "{ metadata_line-column_name }"|. endif. if sy-tabix <> lines( metadata ). e_sql = e_sql && ','. endif. zcl_sql_executor=>append_field( exporting i_meta_line = metadata_line changing c_catalog = e_catalog ). endloop. |
Все готово для формирования валидного sql запроса, т.е. теперь набор вызываемых полей будет соответствовать полям из field catalog’а
Собираем весь запрос, i_top — входящий параметр ограничивающий количество возвращаемых записей.
1 2 3 4 5 |
if i_top > 0. data(top) = |top { i_top }|. endif. e_sql = |select { top } { e_sql } from "{ e_schema }"."{ e_patch }/{ e_model_name }" { request_tail }|. |
Далее осталась создать динамическую внутреннюю таблицу и вызвать SQL запрос
1 2 3 4 5 6 7 8 |
cl_alv_table_create=>create_dynamic_table( exporting it_fieldcatalog = e_catalog importing ep_table = r_table ). zcl_sql_executor=>result( exporting i_query = query i_con_name = i_con_name importing e_comm = e_comm e_status = e_status changing c_data_table = r_table ). |
Результат
В конце всех манипуляций, в <result_table> можно увидеть следующий результат.
