Перейти до вмісту

Структури

Структура (structure) — складний обʼєкт даних, що групує кілька компонентів різних типів під одним іменем. Типовий приклад — адреса: name, street, city належать разом. Структура — базова цегла для роботи з табличними даними: рядок бази даних, рядок внутрішньої таблиці, work area у SELECT — всюди структури.

Складність структури зростає від flat (тільки елементарні компоненти) до nested (є підструктури) до deep (містить внутрішні таблиці, strings або references). Від цієї класифікації залежить, як поводяться присвоєння, порівняння і робота з базою даних.

Локально — через BEGIN OF ... END OF з TYPES (для типу) або DATA (для обʼєкта):

" Оголошення типу
TYPES: BEGIN OF t_addr,
name TYPE string,
street TYPE string,
city TYPE string,
END OF t_addr.
" Обʼєкт на основі типу
DATA addr TYPE t_addr.
" Обʼєкт з одночасним заданням полів і початкових значень
DATA: BEGIN OF person,
name TYPE string VALUE `John`,
age TYPE i VALUE 30,
END OF person.

Компоненти можуть бути будь-якого типу — елементарні, структурні, внутрішні таблиці, data references:

TYPES: BEGIN OF t_order,
id TYPE i,
customer TYPE t_addr, " вкладена структура
items TYPE TABLE OF t_item WITH EMPTY KEY, " внутрішня таблиця
ref TYPE REF TO data, " reference
END OF t_order.

Глобальні структурні типи

Section titled “Глобальні структурні типи”

DDIC дає готові типи — database tables, DDIC structures, CDS view entities. Всі використовуються однаково:

DATA fli TYPE zdemo_abap_fli. " database table як тип
DATA fli_ve TYPE zdemo_abap_fli_ve. " CDS view entity
DATA struc TYPE cl_some_class=>ty_struc. " структура з public-секції класу

Найкоротший шлях — вивести тип з контексту:

DATA(addr1) = VALUE t_addr( name = `John` city = `Kyiv` ).
SELECT SINGLE * FROM zdemo_abap_fli INTO @DATA(fli).
LOOP AT itab INTO DATA(wa). ENDLOOP.

FINAL(...) створює immutable-структуру — після присвоєння компоненти не змінюються:

LOOP AT itab INTO FINAL(wa).
" wa доступна для читання, wa-comp = value заблоковано
ENDLOOP.

Flat — тільки фіксованої довжини елементарні типи. Nested structure теж flat, поки жоден компонент не є deep:

DATA: BEGIN OF flat_s,
num TYPE i,
code TYPE c LENGTH 5,
pr TYPE p LENGTH 8 DECIMALS 2,
END OF flat_s.

Nested — один або більше компонентів — самі структури:

DATA: BEGIN OF addr_n,
BEGIN OF name,
prename TYPE string,
surname TYPE string,
END OF name,
BEGIN OF city,
zip TYPE string,
name TYPE string,
END OF city,
END OF addr_n.
" Доступ: addr_n-name-prename, addr_n-city-zip

Deep — містить внутрішню таблицю, string, xstring або reference. Увага: навіть одна string-компонента робить структуру deep:

DATA: BEGIN OF addr_d,
name TYPE string, " <-- string робить структуру deep
city TYPE string,
END OF addr_d.

Через selector -:

addr-name = `John`.
DATA(city) = addr-city.
IF addr IS INITIAL. ENDIF.
" Nested
addr_n-name-prename = `John`.
" Через data reference — оператор ->
dref->name = `John`. " звичайний синтаксис
dref->*-name = `John`. " повний: спершу розіменувати, потім компонент

Динамічний доступ через ASSIGN

Section titled “Динамічний доступ через ASSIGN”

Компонент може бути вказаний рядком або номером — корисно для generic-обробки:

DATA(comp_name) = `CITY`.
ASSIGN addr-(comp_name) TO FIELD-SYMBOL(<c>). " за іменем (змінною або літералом)
ASSIGN addr-('NAME') TO <c>. " за іменем-літералом
ASSIGN addr-(2) TO <c>. " за номером
ASSIGN addr-(0) TO <c>. " 0 = вся структура
IF sy-subrc <> 0.
" компонент не існує
ENDIF.
" Ітерація по всіх компонентах
DO.
ASSIGN addr-(sy-index) TO <c>.
IF sy-subrc <> 0. EXIT. ENDIF.
" <c> — черговий компонент
ENDDO.

Для сумісних типів — пряме копіювання:

addr = another_addr.
DATA(copy) = addr. " inline з тим самим типом

Основний спосіб побудувати структуру значень:

addr = VALUE #( name = `John` street = `Main 1` city = `Kyiv` ).
" З inline-оголошенням і явним типом
DATA(a) = VALUE t_addr( name = `John` city = `Kyiv` ).
" BASE — зберегти наявні значення, перезаписати вказані
addr = VALUE #( BASE addr street = `New Street` ).
" Без BASE — вказані поля присвоєні, решта initial (поточний вміст втрачається!)
addr = VALUE #( street = `Only Street` ).

Для nested-структур — вкладені VALUE:

addr_n = VALUE #( name = VALUE #( prename = `John` surname = `Pea` )
city = VALUE #( zip = `12345` name = `Kyiv` ) ).

Коли структури різних типів, але з однаково названими компонентами — CORRESPONDING копіює за іменами:

" Initialize + copy matching components
target = CORRESPONDING #( source ).
" BASE — зберегти наявний вміст target, перезаписати однойменні
target = CORRESPONDING #( BASE ( target ) source ).
" MAPPING — явне мапування імен (коли поля називаються по-різному)
target = CORRESPONDING #( source MAPPING new_name = old_name ).
" EXCEPT — виключити компоненти зі зіставлення
target = CORRESPONDING #( source EXCEPT some_field ).

MOVE-CORRESPONDING — statement-еквівалент без ініціалізації цілі (тотожній CORRESPONDING ... BASE):

MOVE-CORRESPONDING source TO target.

Для deep-структур є додаткові модифікатори (EXPANDING NESTED TABLES, KEEPING TARGET LINES, DEEP, APPENDING BASE) — керують обробкою внутрішніх таблиць-компонентів. Деталі — в оригіналі.

CLEAR addr. " всі компоненти у initial
CLEAR addr-name. " один компонент
FREE addr. " CLEAR + звільнити памʼять deep-компонентів
addr = VALUE #( ). " еквівалент CLEAR через VALUE
" SELECT SINGLE — один рядок у flat-структуру
SELECT SINGLE * FROM zdemo_abap_fli
WHERE carrid = 'LH'
INTO @DATA(fli).
" INTO CORRESPONDING FIELDS — коли типи несумісні
SELECT SINGLE * FROM zdemo_abap_fli
WHERE carrid = 'AA'
INTO CORRESPONDING FIELDS OF @my_struc.
" INSERT / UPDATE / MODIFY / DELETE зі структури
INSERT INTO dbtab VALUES @struc.
INSERT dbtab FROM @struc.
UPDATE dbtab FROM @struc.
MODIFY dbtab FROM @struc. " INSERT або UPDATE залежно від ключа
DELETE dbtab FROM @struc.
" Inline конструктор — без наперед оголошеної структури
INSERT dbtab FROM @( VALUE #( comp1 = ... comp2 = ... ) ).

Дозволяють “склеїти” компоненти з іншої структури без створення підструктури:

TYPES: BEGIN OF name_t, prename TYPE string, surname TYPE string, END OF name_t,
BEGIN OF addr_t, street TYPE string, city TYPE string, END OF addr_t.
TYPES BEGIN OF person_t.
INCLUDE TYPE name_t.
INCLUDE TYPE addr_t AS location RENAMING WITH SUFFIX _loc.
TYPES END OF person_t.
" person_t містить: prename, surname, street_loc, city_loc
" street_loc і city_loc доступні також як person-location-street_loc при AS
  • INCLUDE TYPE — включає тип (результат TYPES).
  • INCLUDE STRUCTURE — включає реально існуючий обʼєкт (рідко потрібно).
  • AS name — дає доступ як до “віртуальної” підструктури.
  • RENAMING WITH SUFFIX _x — додає суфікс до імен компонентів, щоб уникнути колізій.

Корисно для DDIC-структур, де кілька глобальних структур мають спільні секції (напр., адреса, аудит-поля) — включаєш замість копіювання.

Вбудована системна структура, заповнюється runtime-ом. Найчастіше вживані компоненти:

  • sy-subrc — return code після багатьох операторів (0 = ОК).
  • sy-tabix — номер рядка внутрішньої таблиці (після READ TABLE, LOOP AT).
  • sy-index — номер ітерації у DO/WHILE.
  • sy-datum / sy-uzeit — поточна дата/час.
  • sy-mandt / sy-langu — мандант і мова сесії.

У ABAP Cloud більшість інших компонентів заборонена — посилаються на legacy-контексти (dynpro, список).

Отримати метадані типу у runtime:

DATA(td) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_data( struc ) ).
LOOP AT td->components INTO DATA(comp).
out->write( |{ comp-name } : { comp-type_kind }| ).
ENDLOOP.

Компонент структури, що сам є структурою, можна оголосити BOXED — ABAP тримає його по reference, економлячи памʼять у випадках, коли такий компонент часто пустий:

TYPES: BEGIN OF t,
id TYPE i,
details TYPE big_struc BOXED,
END OF t.

Семантика доступу не змінюється — struc-details-field працює як звичайно. Особливість — памʼять виділяється лише коли компонент заповнений.

Адаптовано з 02_Structures.md (Apache 2.0). Повний перелік нюансів — в оригіналі.