Struktura i tabela wewnętrzna w SAP ABAP
Artykuł omawia podstawy pracy ze strukturami i tabelami wewnętrznymi w SAP ABAP.
Przydatne linki:
- Character String Data Types | SAP Help Portal
- Predefined ABAP Types
- ABAP - Keyword Documentation | ABAP Keyword Documentation *
*Ta sama dokumentacja jest też dostępna z systemu SAP przez transakcję ABAPDOCU lub z poziomu Eclipse/ADT (F1 na słowie kluczowym otwiera odpowiedni fragment Help Portal).
- ABAP Best practices - GitHub SAP: Clean ABAP - oficjalny styleguide SAP z zasadami pisania czystego kodu (m.in. nazewnictwo zmiennych, klas, metod, długość nazw) : SAP/styleguides: This repository provides SAP style guides for coding and coding-related topics.
Artykuły o naming conventions:
-
ABAP Naming Conventions & Self-Explanatory Code Guide - rozpisane przykłady prefiksów (lv_, ls_, lt_), nazwy klas, interfejsów, testów.
-
Blog „ABAP Code Naming Conventions” - praktyczne propozycje standardu do zastosowania w projekcie (prefiksy, długości, styl camel_case vs. underscores).
-
Dyskusja „Best practices in internal table naming convention (GT, GS, LT, LS)” na SAP Community: Best practices in Internal table naming convention… - SAP Community
Spis treści
- Tworzenie typu (wewnętrznego).
- Tworzenie struktury.
- Tworzenie tabeli wewnętrznej.
TYPY
Typ - opis „jakiego rodzaju” dane mogą być przechowywane w zmiennej albo obiekcie - np. liczba całkowita, tekst, data, struktura czy tabela wewnętrzna.
Typ sam w sobie nie zajmuje pamięci - to tylko definicja, bowiem pamięć zajmuje dopiero zmienna (data object) zadeklarowana z użyciem tego typu.
Typy dzielą się m.in. na:
- elementarne (np. I, P, C, D, T),
- złożone, czyli struktury i tabele wewnętrzne,
- referencyjne, które przechowują odwołania do obiektów.
- Warto tutaj doprecyzować, że wyróżniamy 2 główne typy danych w języku ABAP:
Typy tzw. ABAP‑owe (wbudowane)
- To typy takie jak I, F, P, D, T, C, N, STRING, X, XSTRING itd.
- Używamy ich bezpośrednio w kodzie, np. DATA lv_num TYPE i.
- Są zdefiniowane w samym języku ABAP.
Typy słownikowe (DDIC)
- To typy z ABAP Dictionary: CHAR10, NUMC8, CURR, QUAN, domeny, data elements, struktury, typy tabel itp.
- Występują zarówno w kodzie, jak i w tabelach bazy danych, widokach, interfejsach.
- Przykład: DATA lv_pernr TYPE pernr_d. - tu pernr_d jest typem słownikowym opartym na NUMC.
Jak o typach myśleć w praktyce?
Typ ABAP - prosty "klocek" techniczny, używany głównie wewnątrz logiki (zmienne pomocnicze, liczniki, tymczasowe teksty).
Typ DDIC - „biznesowy” typ, powiązany z modelem danych systemu (numery pracowników, kwoty, ilości, kody walut). Dobre praktyki mówią:
tam, gdzie dane są związane z bazą/systemem, używaj typów DDIC, żeby
wszystko było spójne.
!!! W projektach produkcyjnych preferuje się typy z DDIC, bo zapewniają spójność z tabelami bazy danych i walidacjami.
- Zadanie projektowe
Tworzę tabelę ewidencji czasu pracy, która będzie zbudowana z kolumn: id pracownika, okres (przepracowany miesiąc), liczba przepracowanych godzin.
- Utworzenie nowego programu ABAP w Eclipse:




- Zaczynam pisać kod:
TYPES: BEGIN OF ty_emp_worktime
- TYPES -> mówi kompilatorowi: „za chwilę zdefiniuję nowy typ danych”.
- BEGIN OF -> zaczyna definicję typu strukturalnego (rekord z kilkoma polami). Definicja typu strukturalnego na początku porządkuje dane i umożliwia wielokrotne ich użycie w programie. Typ struktury jest jak „szablon rekordu” tzn. raz opisujesz pola (np. pracownik, data, godziny), potem możesz tworzyć wiele zmiennych lub tabel wewnętrznych dokładnie tego kształtu.
- ty_emp_worktime -> to nazwa typu - tutaj: „typ rekordu czasu pracy pracownika”. Klucz jest taki, żeby po ty_ użyć krótkiego, angielskiego opisu tego, co reprezentuje jeden wiersz (rekord) tabeli - wtedy kod jest od razu zrozumiały.

employee_id TYPE char10,
work_date TYPE d,
hours_work TYPE i,
END OF ty_emp_worktime.
- employee_id –> kolumna z identyfikatorem pracownika.
- work_date –> kolumna z datą pracy.
- hours_work –> kolumna z liczbą przepracowanych godzin
Co to jest CHAR10?
CHAR10 oznacza po prostu łańcuch znaków o stałej długości 20 znaków - typ „character” z długością 20.
Co to jest CHAR?
Bazowy typ to CHAR (character string), a liczba po nim to długość, więc CHAR20 = TYPE c LENGTH 20.
Pola tego typu zawsze mają dokładnie 20 znaków, jeśli wpiszemy krótszą wartość, wówczas reszta zostanie wypełniana spacjami.
Inne często używane typy
Kilka podstawowych wbudowanych typów w ABAP:
- CHARn - tekst o stałej długości, np. CHAR10, CHAR50.
- NUMCn - ciąg cyfrowych znaków o długości n, np. numery dokumentów z zerami wiodącymi.
- I - liczba całkowita (integer).
- P - liczba zapakowana (packed), używana m.in. dla kwot z miejscami po przecinku.
- D - data, T - czas.
W słowniku danych (ABAP Dictionary) są też bardziej „biznesowe” typy zbudowane na tych bazowych, np. ACCP (okres księgowy), CURR (kwota), QUAN (ilość).
Gdzie to jest opisane?
Oficjalnie wszystkie wbudowane typy ABAP są opisane w dokumentacji SAP w sekcji Predefined ABAP Types oraz Data Types.
Z kolei typy słownikowe (Dictionary), takie jak CHAR, NUMC, CURR, są zebrane w rozdziale Predefined Data Types in ABAP Dictionary – tam znajdziesz tabelę z listą typów, maksymalnymi długością.
Podstawowe typy tekstowe w ABAP dla stringów
- c / CHARn – tekst o stałej długości (np. c LENGTH 10, CHAR10), nadaje się na kody, ID, krótkie pola opisowe.
- n / NUMCn – „tekst cyfrowy”, tylko cyfry, też o stałej długości (np. numery z zerami wiodącymi).
- string – tekst o zmiennej długości (dynamiczny), dobry na dłuższe opisy, komunikaty, JSON itp.
- sstring / STRING (w DDIC) - typy słownikowe odpowiadające krótkim/długim tekstom zmiennej długości.
Wszystkie te typy przechowują ciągi znaków, różnią się głównie
długością (stała vs. dynamiczna) i ograniczeniami (np. tylko cyfry w
NUMC).
Tak wygląda nasz kod dla utworzenia opisu - są to tak naprawdę dopiero ramy dla naszej tabeli:

Konstrukcja BEGIN OF … / END OF …**
Zapis END OF ty_emp_worktime. zamyka definicję tego konkretnego typu struktury i jest wymagany przez składnię ABAP.
Po co powtarza się nazwę typu?
Konstrukcja BEGIN OF … / END OF … działa jak nawiasy: mówisz kompilatorowi, że wszystkie pola między nimi należą do jednego typu struktury.
Nazwa po END OF (ty_emp_worktime) mówi dokładnie, którą strukturę kończysz - dzięki temu można w jednym module definiować kilka typów strukturalnych jeden po drugim i kompilator się nie gubi.
BUDOWA STRUKTURY
Teraz będę rozbudowywać kod, tak aby na bazie powyższego kodu utworzyć kolejne “byty” tj. elementy, zacznę od struktury. Rozpoczynamy od zdefiniowania zmiennej dla danych:
DATA: ls_hours_work type ty_emp_worktime.
- DATA -> definiuję zmienną dla danych.
- ls_hours_work -> nazwa zmiennej. W dosłownym tłumaczeniu „lokalna struktura z godzinami pracy”.
- TYPE ty_emp_worktime –> infromacja, że zmienna ma typ struktury, który wcześniej został zdefiniowany jako ty_emp_worktime.
Nazewnictwo zmiennych zwykle prefiks ls_ oznacza local structure:
- ls_ -> zazwyczaj rozwija się do local structure - lokalna struktura (jeden rekord),
- lt_ -> rozwija się do local table - lokalna tabela wewnętrzna (wiele rekordów).
- lv_ -> lokalna zmienna prosta (variable).
ls i lt są całkowicie umowne - język ABAP ich nie wymaga, ale branżowa konwencja bardzo je poleca. Co więcej stosowanie ls/lt/lv wypełnia zasadę. „self-explanatory / easily readable code”.
Możesz nazwać zmienne jakkolwiek: worktime_rec**, gt_hours, employee_data. W wielu firmach używa się innych liter przed _ (np. gs_/gt_ dla zmiennych globalnych, lv_ dla prostych zmiennych), ale zawsze chodzi o to samo: prefiks opisuje rodzaj i zasięg zmiennej.
Po co prefix ty_ ?
Ułatwia on odróżnienie w kodzie:
- ty_emp_worktime - typ (szablon struktury),
- ls_emp_worktime - zmienna (lokalna struktura tego typu).
Dzięki temu, czytając kod, od razu wiesz, czy coś jest „modelem” (typem), czy konkretnymi danymi.
Czy ty_ jest obowiązkowe?
Nie, język ABAP tego nie wymaga - mógłbyś nazwać typ np.
employee_worktime_type. ty_ jest po prostu szeroko stosowaną praktyką
w standardach nazewniczych (obok lv_, ls_, lt_ itd.), która poprawia
czytelność i spójność kodu.
Definicja typu
-
Definicja typu (
TYPES ...)to opis kształtu danych, np. że rekord ma pola employee_id, work_date, hours_work. -
Taki typ nie zajmuje pamięci i nie przechowuje wartości, jest tylko szablonem, którego można używać wiele razy.
Zmienna (obiekt danych)
-
Zmienna (
DATA ... TYPE ...) to konkretny egzemplarz danych w pamięci utworzony na podstawie typu. -
Ma już przydzieloną pamięć i może przechowywać aktualne wartości (np. employee_id = ‘KOWALSKI’).
Podsumowanie:
- typ = „jaki kształt mają dane”,
- zmienna = „konkretne dane tego kształtu przechowywane w pamięci”.
Kontynuujemy pisanie kodu: ls_hours_work - cała struktura (rekord z kilkoma polami).
-employee_id - konkretna składowa tej struktury, jedno pole w środku.
= - ustaw temu polu wartość 'KOWALSKI'.
ls_hours_work-employee_id = 'KOWALSKI'.
ls_hours_work-work_date = '20251206'.
ls_hours_work-hours_work = '5'.
Efekt: powstaje pojedynczy „wiersz” z trzema polami: employee_id, work_date, hours_work, który możemy wypełnić wartościami.
cl_demo_output=>display(
EXPORTING
data = ls_hours_work
).
- display ma w swojej definicji parametr wejściowy o nazwie data.
- Słowo EXPORTING otwiera sekcję „parametry, które przekazuję do metody jako dane wejściowe” - po nim wymieniasz pary nazwa_parametru = zmienna.
- data = ls_hours_work` znaczy: do parametru data przekaż zawartość zmiennej ls_hours_work.
Dlaczego przekazanie wartości do parametru metody jest konieczne? EXPORTING
Przekazanie ls_hours_work do metody jest konieczne, bo w ABAP metoda
nic „z góry” nie wie o Twoich zmiennych - widzi tylko swoje
parametry.
Hermetyzacja metod
- Metoda
displayjest zdefiniowana gdzieś w klasiecl_demo_outputz parametremIMPORTING data TYPE any. - Zgodnie z zasadami programowania strukturalnego/obiektowego ABAP, metoda pracuje wyłącznie na tym, co dostanie w parametrach (i ewentualnie na własnych polach), a nie na „globalnych” zmiennych Twojego raportu.
Dlatego musimy jawnie powiedzieć: „ta metoda ma pracować na strukturze
ls_hours_work” - robimy to przez EXPORTING data = ls_hours_work.
Silne typowanie i jawne interfejsy
-
ABAP jest językiem statycznie typowanym - kompilator musi z góry wiedzieć, jaki typ danych trafia do parametru data.
-
Interfejs metody (
IMPORTING / EXPORTING / CHANGING / RETURNING) opisuje dokładnie, jakie dane i w jakich kierunkach przepływają, bez przekazania wartości parametrów metoda nie ma żadnych „wejść”.
W skrócie:
- definicja metody mówi: „przyjmuję coś w parametrze
data”; - wywołanie z
EXPORTING data = ls_hours_workmówi: „tu masz konkretną zmienną, z którą masz pracować”.
Ta konieczność wynika więc z cech ABAP-u jako języka z silnym typowaniem i jawnymi interfejsami metod - metoda nie widzi Twoich zmiennych, dopóki ich do niej nie przekażesz.

Shift F1
CTRL F2
CTRL F3
F8

Struktura stanowi pierwszą linijkę tabeli: KOWALSKI, 2025-12-06, 5
TWORZENIE TABELI WEWNĘTRZNEJ
Tabela wewnętrzna jest potrzebna po to, aby w programie przechowywać i obrabiać wiele wierszy danych tylko w pamięci, bez ciągłego czytania bazy.
Główne powody użycia tabeli wewnętrznych
- Tymczasowe przechowywanie - pobierasz dane z tabel bazy (np. wynagrodzenia, godziny pracy) jednym lub kilkoma SELECT-ami i trzymasz je w tabeli wewnętrznej podczas działania programu.
- Przetwarzanie wielu rekordów - możesz po niej robić
LOOP, READ TABLE, SORT, DELETE, liczyć sumy, filtrować, łączyć z innymi tabelami itp., tak jakbyś miał mini-bazę tylko dla swojego raportu. - Wydajność i wygoda - zamiast strzelać wieloma SELECT-ami do bazy, robisz jedno pobranie, a reszta logiki dzieje się na danych w pamięci, co jest zwykle szybsze i prostsze w kodzie.
Dla mojego zadania projektowego tabela wewnętrzna jest przydatna, ponieważ dzięki niej tworzę „listę” rekordów czasu pracy, mogę do niej dopisywać kolejne struktury ls_hours_work (np. dla różnych dni lub pracowników), a na końcu jednym wywołaniem wyświetlić lub zapisać wszystkie naraz.
Natomiast tabela wewnętrzna nie sprawdza się gdy nie potrzebujesz
listy wielu rekordów ani dalszego przetwarzania w pamięci.
Typowe sytuacje „bez sensu” dla tworzenia tabeli wewnętrznej
-
Masz tylko jeden rekord - jeśli program zawsze operuje na pojedynczym wpisie (np. jedno wynagrodzenie, jedna data), wystarczy jedna struktura, tabela tylko komplikuje kod.
-
Brak dalszego przetwarzania - gdy od razu wyświetlasz/drukujesz wynik z SELECT-a i nie filtrujesz, nie sortujesz, nie łączysz z innymi danymi, czasem da się obejść bez tabel wewnętrznych (np. prosty WRITE z pojedynczej struktury).
-
Dane są ogromne, a logika prosta - ładowanie miliona wierszy do tabeli wewnętrznej tylko po to, żeby przejść raz pętlą, może zabić pamięć, wtedy lepiej przetwarzać porcjami (SELECT z UP TO n ROWS, kursory) albo przerzucić logikę bliżej bazy (CDS, SQL) lub specjalne techniki buforowania. Przy bardzo dużych wolumenach (setki tysięcy, miliony wierszy) tabela wewnętrzna może zająć dużo pamięci i zwolnić system.
W skrócie: używaj tabel wewnętrznych, gdy potrzebujesz kolekcji
rekordów do obróbki, a gdy masz pojedynczy obiekt lub bardzo prostą
operację, zwykła struktura lub pojedyncza zmienna jest lepszym
wyborem.
Wracając do pisania kodu deklaruję tabelę wewnętrzną lt_hours_work, w której każdy wiersz ma strukturę ty_emp_worktime.

Dodałam kolejne dane:

Dodaję APPEND i na końcu klasę demo output: APPEND przenosi strukturę ls_hours_work do tabeli lt_hours_work.:

Po aktywacji pojawia się pierwsze okno ze strukturą:

Zamykamy okno i pojawia się nasza tabela z obiema zapisami:

Podsumowując dzięki strukturze mogłam dodać dane do tabeli.
Warto jednak dodać, że są nowsze rozwiązania pozwalające na dodanie danych do tabeli. Mówiąc w skrócie SAP rozwija ABAP Platform razem z kolejnymi wersjami NetWeaver/SAP S/4HANA i ABAP Cloud; każda wersja wnosi nowe elementy języka - nowsze wersje języka (ABAP 7.40, 7.50, 7.52, 7.55, ABAP Cloud), oraz „modern ABAP syntax”.
Dodanie wierszy z nowymi danymi może odbywać się dwoma sposobami:
I. Z zastosowaniem #:
‘#’ pozwala na rozpozanie typu danych znajdujących się w nawiasach z tabeli lt_hours_work, a jak wiemy lt_hours_work jest tego typu:



II. Z zastosowaniem typu tabelarycznego t_ty_emp_worktime (STANDARD TABLE OF) - dodaję kod:

TYPES: t_ty_emp_worktime TYPE STANDARD TABLE OF ty_emp_worktime WITH DEFAULT KEY.
- t_ty_emp_worktime - nowy lokalny typ tabeli.
- TYPE STANDARD TABLE OF ty_emp_worktime - tabela standardowa, której każdy wiersz ma typ ty_emp_worktime (Twoja struktura z employee_id, work_date, hours_work).
- WITH DEFAULT KEY - tabela ma domyślny klucz (wszystkie pola struktury są kluczem).
Z kolei kod:
lt_hours_work = VALUE t_ty_emp_worktime(
( employee_id = ‘JANKOWSKA’ work_date = ‘20250707’ hours_work = 10 )
( employee_id = ‘LUBICZ’ work_date = ‘20250302’ hours_work = 3 )
).
- VALUE t_ty_emp_worktime( … ) - utwórz nową tabelę typu t_ty_emp_worktime.
- Każda para nawiasów (…) to jeden wiersz tej tabeli, z przypisaniem pól po nazwie.
Efekt przy zastosowaniu opcji z # lub z zastosowaniem typu tabelarycznego jest ten sam - tj. powstanie dwóch wierszy z nowymi danymi.
Typy lokalne są widoczne tylko w tym programie i dane zawarte tutaj również nie są widoczne poza tym programem - nie można ich użyć poza tym programem.
Dlatego też typy lokalne mają małe zastosowanie, ale sprawdzają się w
małych, jednorazowych strukturach pomocniczych, tymczasowe typy do
raportu, których nie chcemy upubliczniać w całym systemie.