Внутрішні таблиці
Внутрішня таблиця (internal table) — динамічний обʼєкт даних, що тримає у памʼяті послідовність рядків одного типу. Це найчастіше вживана структура даних в ABAP — будь-яка робота з набором записів (з бази, від користувача, згенерованих програмно) зводиться до внутрішньої таблиці.
Три ключові властивості визначають, як таблиця поводиться:
- Line type — тип одного рядка. Зазвичай це структура, але може бути й елементарний тип (наприклад, таблиця рядків
string_table). - Table category — механізм внутрішнього зберігання:
STANDARD,SORTEDабоHASHED. Від цього залежать доступні способи доступу і швидкість. - Table key — одне або кілька полів, за якими оптимізовано пошук. Primary key є завжди, secondary keys — опційні.
Table category
Section titled “Table category”Від категорії залежить як продуктивність, так і доступні операції. Обирай її відповідно до того, як таблиця буде використовуватись.
| Категорія | Доступ | Primary key | Коли брати |
|---|---|---|---|
STANDARD | index, key | non-unique або empty | послідовна обробка, додавання в кінець, індексний доступ |
SORTED | index, key | unique або non-unique | автосортування при вставці, швидкий пошук за ключем (бінарний) |
HASHED | тільки key | лише unique | великі таблиці з частим доступом за ключем (O(1)) |
STANDARD — дефолтний вибір: дешева вставка в кінець, але лінійний пошук. SORTED підтримує впорядкування автоматично, тому кожна вставка коштує O(log n) — вигідно, коли даних багато і ми часто шукаємо. HASHED — для випадків, коли доступ завжди за ключем і ніколи за індексом.
Primary key існує завжди. Для STANDARD може бути EMPTY (тобто ключа нема — пошук тільки за індексом або через WHERE). Для SORTED/HASHED ключ впливає на внутрішнє впорядкування, тому обов’язковий.
Secondary keys дають альтернативні оптимізовані шляхи доступу без зміни primary — корисно, коли одна і та сама таблиця обробляється різними способами.
Оголошення
Section titled “Оголошення”Загальний шаблон:
TYPES itab_type TYPE STANDARD TABLE OF data_type WITH KEY ...DATA itab TYPE itab_type.Якщо категорія не вказана — STANDARD TABLE за замовчуванням.
Варіанти задання ключа:
" Усі символьні поля структури автоматично стають ключемDATA it1 TYPE TABLE OF zdemo_flsch WITH DEFAULT KEY.
" Явний primary key з одного або кількох полівDATA it2 TYPE TABLE OF zdemo_flsch WITH KEY carrid.
" Без ключа взагалі — коли ключ не потрібен і ми хочемо" уникнути випадкових повільних пошуків за DEFAULT KEYDATA it3 TYPE TABLE OF zdemo_flsch WITH EMPTY KEY.
" Primary empty + додатковий sorted secondary для швидкого пошукуDATA it4 TYPE TABLE OF zdemo_flsch WITH EMPTY KEY WITH UNIQUE SORTED KEY cities COMPONENTS cityfrom cityto.WITH DEFAULT KEY — історична спадщина, зручно виглядає, але може створювати дивні ключі автоматично. У сучасному коді краще WITH EMPTY KEY або явний WITH KEY ....
Inline-оголошення (7.40+)
Section titled “Inline-оголошення (7.40+)”Не оголошуємо тип наперед — компілятор виводить його з правої частини:
DATA(itab) = VALUE string_table( ( `text1` ) ( `text2` ) ).FINAL(ro) = itab. " імутабельна копіяSELECT * FROM tab INTO TABLE @DATA(itab3).FINAL(...) створює read-only локальну змінну — ніхто далі не зможе випадково модифікувати таблицю.
Наповнення
Section titled “Наповнення”Копіювання
Section titled “Копіювання”itab = itab2. " існуючий вміст itab перезаписуєтьсяDATA(itab3) = itab. " inline-копіяКопіювання — по значенню. Зміни в itab3 не впливають на itab.
INSERT і APPEND
Section titled “INSERT і APPEND”APPEND і INSERT виглядають схоже, але важлива різниця у тому, куди додається рядок і для яких категорій працює:
" APPEND — тільки для STANDARD, завжди в кінецьAPPEND VALUE #( comp1 = a comp2 = b ) TO itab.APPEND INITIAL LINE TO itab.
" INSERT — працює для всіх категорій" Для SORTED сам знайде правильну позицію за ключемINSERT VALUE #( comp1 = a comp2 = b ) INTO TABLE itab.
" З явним індексом — тільки STANDARDINSERT VALUE #( comp1 = a comp2 = b ) INTO itab INDEX 5.
" Масове додавання з іншої таблиціAPPEND LINES OF itab2 TO itab.APPEND LINES OF itab2 FROM 3 TO 5 TO itab.Правило памʼяті: APPEND = “додати в кінець списку”, INSERT INTO TABLE = “додати з урахуванням категорії/ключа”.
Конструкторські вирази
Section titled “Конструкторські вирази”VALUE будує таблицю з літералів або комбінує кілька джерел у одному виразі:
itab = VALUE #( ( comp1 = a comp2 = b ) ( comp1 = c comp2 = d ) ).
" BASE — не очищати, дописати до існуючого вмістуitab = VALUE #( BASE itab ( comp1 = e comp2 = f ) ).
" Злити кілька джерел в одну таблицюitab = VALUE #( ( LINES OF itab2 ) ( LINES OF itab3 FROM 2 TO 5 ) ).Без BASE таблиця повністю перезаписується, навіть якщо ти додаєш один рядок. Часта помилка на стрес-інтервʼю.
CORRESPONDING копіює з автоматичним мапінгом за іменами полів — незамінне для обміну даних між несумісними структурами:
itab = CORRESPONDING #( itab2 ). " за збігом імен полівitab = CORRESPONDING #( BASE ( itab ) itab2 ). " зберегти наявний вмістitab = CORRESPONDING #( itab2 MAPPING a = c b = d ). " явний мапінг іменitab = CORRESPONDING #( itab2 EXCEPT e ). " виключити одне полеitab = CORRESPONDING #( itab2 DISCARDING DUPLICATES ). " пропустити дублі за ключемFILTER — читає тільки частину рядків за умовою:
DATA(f1) = FILTER #( itab WHERE comp1 >= 3 ).DATA(f2) = FILTER #( itab EXCEPT WHERE comp1 >= 3 ).DATA(f3) = FILTER #( itab USING KEY sec_key WHERE comp1 = 3 ).FILTER — швидкий (особливо з sorted-ключем), бо не копіює всю таблицю у память перед фільтрацією.
Читання одного рядка
Section titled “Читання одного рядка”Три способи залежно від того, що треба: читати дані, модифікувати через field symbol або працювати з data reference.
За індексом
Section titled “За індексом”" Читання у work area — повна копія рядкаREAD TABLE itab INTO wa INDEX 1.
" Через field symbol — працюємо напряму з рядком таблиці, без копіюванняREAD TABLE itab ASSIGNING FIELD-SYMBOL(<fs>) INDEX 2.
" Через data reference — коли потрібен вказівникREAD TABLE itab REFERENCE INTO DATA(dref) INDEX 3.
" Табличний вираз table expression (7.40+) — коротшеDATA(line) = itab[ 1 ].DATA(comp) = itab[ 2 ]-field_name.
" Безпечне читання без exceptionDATA(safe) = VALUE #( itab[ 5 ] OPTIONAL ).DATA(with_default) = VALUE #( itab[ 6 ] DEFAULT itab[ 1 ] ).За table key
Section titled “За table key”Якщо таблиця має ключ — найшвидший спосіб:
READ TABLE itab INTO wa WITH TABLE KEY primary_key COMPONENTS a = 1 b = 2.READ TABLE itab INTO wa WITH TABLE KEY sec_key COMPONENTS c = 3.
" Те саме через table expressionDATA(line) = itab[ TABLE KEY primary_key a = 1 b = 2 ].DATA(line2) = itab[ TABLE KEY sec_key c = 3 ].За вільним ключем (WITH KEY)
Section titled “За вільним ключем (WITH KEY)”Якщо ключа на цих полях немає — ABAP зробить лінійний пошук по STANDARD або бінарний по SORTED:
READ TABLE itab INTO wa WITH KEY comp1 = value.DATA(line) = itab[ comp1 = value ].Системні поля після READ TABLE
Section titled “Системні поля після READ TABLE”Після READ TABLE (і LOOP AT) заповнюються:
| Поле | Значення |
|---|---|
sy-subrc | 0 — знайдено, 4 — не знайдено (STANDARD/HASHED), 8 — не знайдено (SORTED) |
sy-tabix | індекс рядка (для primary/sorted-ключів; 0 для HASHED) |
Звичка завжди перевіряти sy-subrc після READ TABLE — рятує від дампів.
WHERE у READ TABLE
Section titled “WHERE у READ TABLE”READ TABLE itab INTO wa WHERE comp1 > 3.READ TABLE itab INTO wa WHERE comp2 CS 'підрядок'.READ TABLE itab INTO wa WHERE comp1 BETWEEN 5 AND 10.
" Придушити performance-warning свідомо, якщо так і требаREAD TABLE itab INTO wa WHERE comp1 = value ##read_where_ok.Модифікація
Section titled “Модифікація”" За індексомMODIFY itab FROM wa INDEX 1.MODIFY itab FROM wa INDEX 2 TRANSPORTING comp1 comp2. " змінити тільки ці поля
" За ключемMODIFY itab FROM wa USING KEY primary_key.
" Через field symbol у циклі — найшвидшеLOOP AT itab ASSIGNING FIELD-SYMBOL(<fs>). <fs>-comp1 = new_value. " зміна напряму в таблиціENDLOOP.Видалення
Section titled “Видалення”DELETE itab INDEX 1.DELETE itab WHERE comp1 = value.DELETE itab FROM 2 TO 5.
" Видалення дублікатівSORT itab BY comp1. " <-- обовʼязковоDELETE ADJACENT DUPLICATES FROM itab COMPARING comp1.DELETE ADJACENT DUPLICATES FROM itab COMPARING ALL FIELDS.
" Очистити повністю — обидва варіанти еквівалентніCLEAR itab.DELETE itab.Три типи доступу всередині циклу — той самий вибір, що й у READ TABLE:
" Копія у work area — безпечно, але повільніше для великих таблицьLOOP AT itab INTO wa. " Робота з копією; зміни wa не впливають на таблицюENDLOOP.
" Через field symbol — без копіювання, зміни впливають напрямуLOOP AT itab ASSIGNING FIELD-SYMBOL(<fs>). <fs>-comp = value. " модифікація рядка таблиціENDLOOP.
" Через data reference — коли потрібен вказівник (напр., передати в метод)LOOP AT itab REFERENCE INTO DATA(dref). " Доступ через dref->compENDLOOP.Обмеження діапазону
Section titled “Обмеження діапазону”LOOP AT itab INTO wa FROM 3 TO 7. ENDLOOP. " тільки рядки 3..7LOOP AT itab BACKWARD INTO wa. ENDLOOP. " у зворотному порядкуLOOP AT itab INTO wa USING KEY sec_key. ENDLOOP. " обхід за secondary keyLOOP AT itab INTO wa FROM 1 TO 10 STEP 2. ENDLOOP. " через одинКерування потоком у циклі
Section titled “Керування потоком у циклі”LOOP AT itab INTO wa. IF wa-flag = 'X'. EXIT. " повний вихід з циклу ENDIF. IF wa-skip = 'X'. CONTINUE. " пропустити цей рядок, перейти до наступного ENDIF. " ... обробка ...ENDLOOP.Сортування
Section titled “Сортування”SORT itab. " за primary keySORT itab BY comp1 comp2 DESCENDING.SORT itab BY comp1 ASCENDING comp2 DESCENDING.Для SORTED таблиць SORT не потрібен і не має сенсу — вона вже відсортована за ключем. Для STANDARD — ручне сортування, коли треба.
Інформація про таблицю
Section titled “Інформація про таблицю”" Чи існує рядок за умовою — без виключеньIF line_exists( itab[ comp1 = value ] ). ...ENDIF.
" Кількість рядківDATA(n) = lines( itab ).
" Рефлексія — отримати метадані про тип таблиціDATA(td) = cl_abap_typedescr=>describe_by_data( itab ).line_exists( ) — безпечний спосіб перевірити наявність рядка без TRY/CATCH або READ TABLE з перевіркою sy-subrc.
ABAP SQL і внутрішні таблиці
Section titled “ABAP SQL і внутрішні таблиці”" Як ціль SELECTSELECT * FROM dbtab INTO TABLE @itab.SELECT col1, col2 FROM dbtab INTO TABLE @itab WHERE cond.
" Як джерело для WHERE ... INSELECT * FROM other WHERE id IN @itab.
" Як джерело запиту (7.56+) — SELECT безпосередньо з internal tableSELECT * FROM @itab AS t WHERE t~comp1 = value INTO TABLE @DATA(res).Зверни увагу на @ перед іменем — це escape для ABAP-змінних у ABAP SQL.
Secondary keys
Section titled “Secondary keys”Основна ідея: одна й та сама таблиця обробляється по-різному у різних місцях коду. Замість того, щоб робити кілька копій або сортувати, заводимо secondary key:
DATA it TYPE TABLE OF struc WITH EMPTY KEY WITH NON-UNIQUE SORTED KEY sec_sorted COMPONENTS a b WITH UNIQUE HASHED KEY sec_hashed COMPONENTS c.
READ TABLE it WITH TABLE KEY sec_sorted COMPONENTS a = 1 b = 2.LOOP AT it USING KEY sec_hashed.ENDLOOP.Виграш у продуктивності:
SORTEDsecondary — бінарний пошук, O(log n)HASHEDsecondary — доступ за константний час, O(1)- Додають індексний доступ навіть до HASHED-таблиць (primary у HASHED не підтримує індексний)
Мінус — додаткова память і час на підтримку secondary-ключа при вставці/видаленні. Має сенс, коли таблиця читається значно частіше, ніж змінюється.
Типові патерни
Section titled “Типові патерни”Зміна рядків у циклі (без MODIFY):
LOOP AT itab ASSIGNING FIELD-SYMBOL(<fs>). <fs>-component = new_value.ENDLOOP.Трансформація у нову таблицю через FOR:
DATA(transformed) = VALUE ty_target( FOR line IN source_table ( target_comp = line-source_comp ) ).Фільтр за умовою:
DATA(filtered) = FILTER #( itab WHERE comp1 >= 100 AND comp2 = 'X' ).Групування і агрегація: див. окрему сторінку Групування внутрішніх таблиць.
Адаптовано з 01_Internal_Tables.md (Apache 2.0). Повний перелік нюансів — в оригіналі.