Условная группировка itab, аналогично предложению HAVING в SQL - PullRequest
1 голос
/ 22 мая 2019

Это дополнительный вопрос для этого , но вместо агрегации я хочу обрабатывать группы на основе некоторого условия и не могу определить правильный синтаксис для этого.Мне нужно исключить группы, которые содержат документы со статусом «удален», если хотя бы один из членов группы имеет этот статус.

Я пробовал до сих пор GROUP...WITHOUT MEMBERS, LOOP...FOR GROUPS, REDUCE, и это решение, которое я в итоге получаю

DATA(lt_valid_doc) = VALUE tt_struct( 
      FOR ls_valid IN VALUE tt_struct( 
             FOR GROUPS <group_key> OF <wa> IN lt_ilot
             GROUP BY ( guid = <wa>-guid guid2 = <wa>-guid2 ) ASCENDING
             LET not_deleted = REDUCE #( INIT valid TYPE t_ref_s_struct
                                         FOR <m> IN GROUP <group_key>
                                         NEXT valid = COND #( 
                                               WHEN valid IS NOT BOUND OR <m>-stat = 'I1040' 
                                               THEN REF #( <m> ) ELSE valid ) )
             IN ( not_deleted->* ) )
      WHERE ( status NE 'I1040' ) 
      ( ls_valid ) ).

Однако это решение кажетсяизбыточный для меня (фильтр I1040 указан дважды).Существует ли какой-либо синтаксис, позволяющий сделать это в одном операторе (REDUCE, GROUP или чем-либо еще), не создавая вложенную таблицу на лету и не фильтруя ее, как я делаю сейчас?

Если я использую WHERE условие для всех вышеприведенных операторов (GROUP...WITHOUT MEMBERS, LOOP...FOR GROUPS и REDUCE) - оно фильтрует только строки base для группировки, а не сами группы.Мне нужно что-то похожее на HAVING в SQL.

UPDATE ОК, вот реальный компилируемый пример на основе таблицы BSEG.Задача состоит в том, чтобы найти только не переписанные документы, т.е. исключить все документы с перевернутыми (XNEGP = true) строками.

TYPES: BEGIN OF t_s_bseg,
         bukrs  TYPE bseg-bukrs,
         belnr  TYPE bseg-belnr,
         gjahr  TYPE bseg-gjahr,
         buzei  TYPE bseg-buzei,
         xnegp  TYPE bseg-xnegp,
       END OF t_s_bseg,
       tt_bseg TYPE SORTED TABLE OF t_s_bseg WITH EMPTY KEY.
TYPES: t_ref_s_bseg TYPE REF TO t_s_bseg.

DATA(lt_valid_fi_doc) = VALUE tt_bseg( 
  FOR ls_valid IN VALUE tt_bseg( 
         FOR GROUPS <group_key> OF <wa> IN lt_bseg
         GROUP BY ( bukrs = <wa>-bukrs belnr = <wa>-belnr gjahr = <wa>-belnr ) ASCENDING
         LET not_reversed = REDUCE #( INIT valid TYPE t_ref_s_bseg
                                     FOR <m> IN GROUP <group_key>
                                     NEXT valid = COND #( 
                                           WHEN valid IS NOT BOUND OR <m>-xnegp = abap_true 
                                           THEN REF #( <m> ) ELSE valid ) )
         IN ( not_reversed->* ) )
  WHERE ( xnegp NE abap_true ) 
  ( ls_valid ) ).

Входные строки

bukrs belnr       gjahr buzei xnegp
1000  0100000001  2019  1 
1000  0100000001  2019  2
1000  0100000003  2019  1
1000  0100000003  2019  2
1000  0100000004  2019  1
1000  0100000004  2019  2     X

Док 0100000004 имеет перевернутую строку, поэтому результат должен быть

bukrs belnr       gjahr buzei xnegp
1000  0100000001  2019   
1000  0100000003  2019

Ответы [ 3 ]

1 голос
/ 23 мая 2019

Вот решение, которое не повторяет выбор, но остается один вопрос, действительно ли это «лучше»?

Решение основано на создании пустой строки, если группа строк содержит одну строку сстатус «I1040» вместо сохранения нежелательной строки.Я не уверен, но, возможно, другое подобное решение могло бы сохранить ссылку на строку (not_deleted), плюс добавив вспомогательную переменную, чтобы узнать, должна ли ссылка оставаться или нет.Я нашел более интуитивно понятным использование табличных индексов (INDEX INTO), но это может не сработать, если tt_struct является типом хэшированной таблицы.

Я предоставляю код с помощью ABAP Unit Test, чтобы вы могли быстроПопробуйте сами.

CLASS ltc_main DEFINITION FOR TESTING
      DURATION SHORT RISK LEVEL HARMLESS.
  PRIVATE SECTION.
    METHODS test FOR TESTING.
    METHODS cut.
    TYPES : BEGIN OF ty_struct,
              guid TYPE string,
              stat TYPE string,
            END OF ty_struct,
            tt_struct      TYPE STANDARD TABLE OF ty_struct WITH EMPTY KEY,
            t_ref_s_struct TYPE REF TO ty_struct.
    DATA: lt_ilot      TYPE tt_struct,
          lt_valid_doc TYPE tt_struct.
ENDCLASS.

CLASS ltc_main IMPLEMENTATION.
  METHOD cut.
    lt_valid_doc = VALUE #(
          FOR ls_valid IN VALUE tt_struct(
                 FOR GROUPS <group_key> OF <wa> IN lt_ilot
                 GROUP BY ( guid = <wa>-guid ) ASCENDING
                 LET x1 = REDUCE #(
                        INIT x2 = 0
                        FOR <m> IN GROUP <group_key> INDEX INTO x3
                        NEXT x2 = COND #(
                              WHEN <m>-stat = 'I1040' THEN -1
                              ELSE COND #( WHEN x2 <> 0 THEN x2 ELSE x3 ) ) )
                 IN ( COND #( WHEN x1 <> -1 THEN lt_ilot[ x1 ] ) ) )
          WHERE ( table_line IS NOT INITIAL )
          ( ls_valid ) ).
  ENDMETHOD.

  METHOD test.

    lt_ilot = VALUE #(
        ( guid = 'A' stat = 'I1000' )
        ( guid = 'A' stat = 'I1040' )
        ( guid = 'B' stat = 'I1020' )
        ( guid = 'C' stat = 'I1040' )
        ( guid = 'D' stat = 'I1040' )
        ( guid = 'D' stat = 'I1000' ) ).

    cut( ).

    cl_abap_unit_assert=>assert_equals( act = lt_valid_doc
          exp = VALUE tt_struct( ( guid = 'B' stat = 'I1020' ) ) ).

  ENDMETHOD.
ENDCLASS.
0 голосов
/ 25 мая 2019

Я действительно надеюсь, что это был какой-то личный тестовый пример, и вы не используете этот код в продуктивной среде.Если бы мне нужно было понять, чего вы хотите достичь, глядя только на кодировку, я бы вас ненавидел;).

Ключом, который легко решает эту проблему, является сортировка таблицы так, чтобы условие удаления всегда было впервая строка группы, которую вы хотите обработать:

Solution1 с выводом уникального списка:

DATA: lt_bseg TYPE STANDARD TABLE OF t_s_bseg.

SORT lt_bseg BY belnr xnegp DESCENDING.
DELETE ADJACENT DUPLICATES FROM lt_bseg COMPARING belnr.
DELETE lt_bseg WHERE xnegp = abap_true.

Solution2

с выводом неуникального списка:

DATA: lt_bseg TYPE STANDARD TABLE OF t_s_bseg,
      lf_prev_belnr TYPE belnr,
      lf_delete TYPE char1.

SORT lt_bseg BY belnr xnegp DESCENDING.

LOOP AT lt_bseg ASSIGNING FIELD-SYMBOL(<ls_bseg>).

    IF <ls_bseg>-belnr <> lf_prev_belnr.
        lf_delete = <ls_bseg>-xnegp.
        lf_prev_belnr = <ls_bseg>-belnr.
    ENDIF.

    IF lf_delete = abap_true.
        DELETE lt_bseg.
    ENDIF.

ENDLOOP.
0 голосов
/ 23 мая 2019

следующее решение может быть не самым красивым, а простым. Мне проще удалить целую группу, если один участник удовлетворяет условию, чем добавить целую группу, если все они не соответствуют условию. Просто идея.

TYPES : BEGIN OF ty_struct,
          guid TYPE string,
          stat TYPE string,
        END OF ty_struct,
        tt_struct TYPE STANDARD TABLE OF ty_struct WITH EMPTY KEY.


DATA(lt_ilot) = VALUE tt_struct(
    ( guid = 'A' stat = 'I1000' )
    ( guid = 'A' stat = 'I1040' )
    ( guid = 'B' stat = 'I1020' )
    ( guid = 'C' stat = 'I1040' )
    ( guid = 'D' stat = 'I1040' )
    ( guid = 'D' stat = 'I1000' )).

LOOP AT lt_ilot INTO DATA(ls_ilot) WHERE stat = 'I1040'.
  DELETE lt_ilot WHERE guid = ls_ilot-guid.
ENDLOOP.
...