1
votes

Clause itab de regroupement conditionnel HAVING dans SQL

Ceci est une question de suivi pour celle-ci a > mais au lieu de l'agrégation, je veux traiter des groupes en fonction de certaines conditions et je ne peux pas comprendre la syntaxe appropriée pour cela. Je dois exclure les groupes qui contiennent des documents avec le statut "supprimé", si au moins un des membres du groupe a ce statut.

J'ai essayé jusqu'à présent GROUPE ... SANS MEMBRES , BOUCLE ... POUR LES GROUPES , REDUCE et c'est la solution avec laquelle je me retrouve

bukrs belnr       gjahr buzei xnegp
1000  0100000001  2019   
1000  0100000003  2019

Â

Cependant, cette solution me semble redondante (filtre I1040 indiqué deux fois). Existe-t-il une syntaxe permettant de faire cela en une seule instruction ( REDUCE , GROUP ou autre) sans construire une table imbriquée à la volée et la filtrer comme je le fais maintenant?

Si j'utilise la condition WHERE sur toutes les instructions ci-dessus ( GROUP ... WITHOUT MEMBERS , LOOP ... FOR GROUP et REDUCE ), il ne filtre que les lignes de base pour le regroupement et non les groupes eux-mêmes. J'ai besoin d'un peu similaire à HAVING dans SQL.

UPDATE OK, voici un exemple compilable basé sur la table BSEG. La tâche consiste à ne trouver que les documents non révérés, c'est-à-dire d'exclure tous les documents avec des lignes inversées (XNEGP = true).

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

Lignes d'entrée

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 ) ).


2 commentaires

Cela aiderait beaucoup si c'était un exemple compilable avec toutes les dépendances fournies.


@Jagger a ajouté un exemple basé sur FI, mon cas vient de SRM donc difficilement reproductible par tout le monde


3 Réponses :


1
votes

Voici une solution qui ne répète pas la sélection, mais une question demeure, est-ce vraiment "mieux"?

La solution est basée sur la génération d'une ligne vide si le groupe de lignes contient une ligne avec le statut 'I1040 ', au lieu de garder la ligne indésirable. Je ne suis pas sûr, mais peut-être qu'une autre solution similaire pourrait conserver la référence à la ligne ( not_deleted ), en plus d'ajouter une variable auxiliaire pour savoir si la référence est à conserver ou non. J'ai trouvé plus intuitif d'utiliser les index de table ( INDEX INTO ), mais cela pourrait ne pas fonctionner si tt_struct est un type de table hachée.

Je fournis le code avec un test unitaire ABAP afin que vous puissiez l'essayer rapidement vous-même.

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.


2 commentaires

Hou la la! Je ne suis pas très expérimenté avec la syntaxe REDUCE et c'est une nouveauté pour moi de pouvoir construire des lignes avec des conditions de manière récursive, comme ceci NEXT x2 = COND # (WHEN stat = ... THEN -1 ELSE COND # (WHEN x2 < > 0 ALORS x2) . Dans l'ensemble, je pense que votre solution est moins lisible et moins concise que la mienne)) Bien qu'elle ne filtre le statut qu'une seule fois, elle n'a cependant pas pu se débarrasser de la construction de tables imbriquées


La condition x1 <> -1 est littéralement la même que le second filtre d'état, donc pas de gain du tout, ni de performance ni de lisibilité.



-2
votes

La solution suivante n'est peut-être pas la plus jolie, mais elle est simple. Il est plus facile de supprimer un groupe entier si un membre remplit une condition que d'ajouter un groupe entier si tous échouent à la condition. Juste une idée.

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.


2 commentaires

Votre solution n'inclut pas de regroupement, plusieurs lignes avec le même guid sont possibles.


Si j'ai bien compris la demande de l'OP, le résultat attendu est la ligne unique "B I1020" (toutes les lignes des groupes avec I1040 doivent être supprimées).



-1
votes

J'espère vraiment que c'était une sorte de cas de test personnel, et vous n'utilisez pas ce codage dans un environnement productif. Si je devais comprendre ce que vous voulez réaliser en regardant uniquement le codage, je vous détesterais;).

La clé pour résoudre facilement ce problème est de trier le tableau de sorte que la condition de suppression soit toujours dans la première ligne de le groupe que vous souhaitez traiter:

Solution1 avec sortie de liste unique:

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.

Solution2

avec sortie de liste non unique:

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.


2 commentaires

Je vous détesterais Je m'en fous :) Cela fonctionne et c'est l'essentiel.


Et btw, votre solution1 ne fonctionne que pour le cas primitif avec un champ binaire comme XNEGP, l'exemple principal avec un statut basé sur une chaîne I1040 ne fonctionne pas avec votre tri.