3

In ABAP, I have a pretty large internal table, say 31,000 rows. What's the shortest and most efficient way to split that into multiple smaller tables of fixed size, say 1,000 rows each?

Naive way would be:

DATA lt_next_package TYPE tt_table_type.
LOOP AT it_large_table INTO DATA(ls_row).
  INSERT ls_row INTO TABLE lt_next_package.
  IF lines( lt_next_package ) >= lc_package_size.
    INSERT lt_next_package INTO TABLE rt_result.
    CLEAR lt_next_package.
  ENDIF.
ENDLOOP.
IF lt_next_package IS NOT INITIAL.
  INSERT lt_next_packge INTO TABLE rt_result.
ENDIF.

That works and is rather efficient, but looks cumbersome, esp. the don't-forget-the-last-package section at the very end. I believe there must be a better way to do this with the newer ABAP mesh paths and table expressions, but so far couldn't come up with one.

Sandra Rossi
  • 11,934
  • 5
  • 22
  • 48
Florian
  • 4,821
  • 2
  • 19
  • 44
  • I'm not sure if it's a proper question. I'm not saying it's a non-valid one, I'm wondering if it's a proper one in the sense: must we find a "shortest" syntax or we must gather for the most efficient one? As a developer, my main goal is to find a balance between efficiency and readability, and not to care about code lenght (although longer code drives to worse readability). – VXLozano Apr 20 '18 at 06:59

4 Answers4

1

I am not sure if, there is a right way to do it (sure, there are several ways to do it), but you can try this to overcome the last package problem:

WHILE it_large_table IS NOT INITIAL.
  LOOP AT it_large_table ASSIGNING FIELD-SYMBOL(<ls_line>) FROM 1 TO 1000.
    INSERT <ls_line> INTO TABLE lt_next_package.
  ENDLOOP.
  DELETE it_large_table FROM 1 TO 1000.
  INSERT lt_next_package INTO TABLE rt_table.
  CLEAR: lt_next_package.
ENDWHILE.
József Szikszai
  • 4,791
  • 3
  • 14
  • 24
1

Based on JozsefSzikszai's answer, devised another option:

rt_result = VALUE #( FOR i = 1
                     UNTIL i > round( val = lines( it_large_table) / lc_package_size
                                      dec = 0
                                      mode = cl_abap_math=>round_up )
                     LET lv_end = i * lc_package_size
                         lv_start = lv_end - lc_package_size + 1 IN
                       ( VALUE <result-type>(
                          ( LINES OF it_large_table FROM lv_start TO lv_end ) ) ) ).
Florian
  • 4,821
  • 2
  • 19
  • 44
  • Where do you have the code that has to be executed for each subset? Written this way, the code is a complicated rt_result = it_large_table. – kdobrev Dec 30 '20 at 16:46
  • That code runs afterwards. It is not inlined into this statement. The idea is to turn a "flat" table of rows into a nested table of tables. Each row in the result is another table that contains one "package". In array notation, what I want is: [a, b, c, d, ...] -> [[a, b], [c, d], ...] – Florian Jan 18 '21 at 09:55
0

Somewhat reinvention of both Florian and Jozsef approaches.

Prerequisits:

TYPES:
  BEGIN OF line,
    rows TYPE string,
    slice TYPE bseg_t,
  END OF line,
  itab TYPE STANDARD TABLE OF line WITH EMPTY KEY,
  bseg_t TYPE STANDARD TABLE OF bseg WITH EMPTY KEY.
DATA: result TYPE itab.

Filling large table:

SELECT * UP TO 31000 ROWS
  INTO TABLE @DATA(lt_bseg)
  FROM bseg.

Here we costruct table of tables which contains slices of the main table by 1000 rows each.

WHILE lt_bseg IS NOT INITIAL.
  result = VALUE itab( BASE result
                       (
                       rows  = | { sy-index * 1000 }-{ sy-index * 1000 + 1000} |
                       slice = VALUE bseg_t( FOR wa IN lt_bseg INDEX INTO i FROM i + 1 TO i + 1
                                             ( LINES OF lt_bseg from i TO i + 999 ) )
                       )
                     ).
  DELETE lt_bseg FROM 1 TO 1000.
ENDWHILE.

Looks somewhat as our requirement, no?

enter image description here

Suncatcher
  • 10,355
  • 10
  • 52
  • 90
  • The indexing is not correct here because you start from row 1 and its written 1000-2000, although the data in the begining is correct. Later on you start to miss some records from the data. Not sure where it comes from, but is the example cannot be regarded as correct. – kdobrev Dec 30 '20 at 14:21
  • Feel free to improve, SO community always encourages the members to find errors in answers and improve – Suncatcher Dec 30 '20 at 15:47
0

Here are two ways to build a table of subtables AKA pagination:

METHOD prepare_data.
TYPES:
  BEGIN OF line,
    name  TYPE string,
    subset TYPE salv_t_row,
  END OF line,
  itab TYPE STANDARD TABLE OF line WITH EMPTY KEY.
CONSTANTS: lc_package_size TYPE i VALUE 3.
DATA: result  TYPE itab,
      it_rows TYPE salv_t_row.

DO round( val = lines( it_rows ) / lc_package_size
          dec = 0
          mode = cl_abap_math=>round_up ) TIMES.

  DATA(lv_end) = sy-index * lc_package_size.
  DATA(lv_start) = lv_end - lc_package_size + 1.
  APPEND INITIAL LINE TO result ASSIGNING FIELD-SYMBOL(<subset>).
  <subset>-name = | Subset { sy-index } |.
  <subset>-subset = VALUE #( ( LINES OF it_rows FROM lv_start TO lv_end ) ).
ENDDO.

clear result.

result = VALUE itab( FOR i = 1
                     UNTIL i > round( val = lines( it_rows ) / lc_package_size
                                   dec = 0
                                   mode = cl_abap_math=>round_up )
                     LET k = 1 IN
                     (
                      name = | Subset { i } |
                      subset = VALUE salv_t_row(
                                               LET
                                                  end = i * lc_package_size
                                                  start = end - lc_package_size + 1
                                               IN
                                                  ( LINES OF it_rows from start to end )
                                               )
                     )
                   ).

Hope this helps. The code here is tested and works. Just copy-paste (and generate some data).

kdobrev
  • 270
  • 1
  • 3
  • 11