Doppelte Einträge aus einer internen Tabelle löschen anhand einer Bedingung

Getting started ... Alles für einen gelungenen Start.
6 Beiträge • Seite 1 von 1
6 Beiträge Seite 1 von 1

Doppelte Einträge aus einer internen Tabelle löschen anhand einer Bedingung

Beitrag von Sonne1234 (ForumUser / 30 / 16 / 1 ) » 20.01.2020 13:33
Hallo zusammen,

ich selektiere Daten aus einer internen Tabelle. In der Tabelle können sich jedoch die Einträge mit OBJKY wiederholen, da es evtl. zunächst einen Status VSTAT mit fehlerhaft (Status=2) gibt und anschließend einen zweiten, wenn eine Nachricht erfolgreich verarbeitet wurde. (Status=1)

Für meine weitere Verarbeitung benötige ich jedoch nur die Einträge, die den Status 1 haben. Ich möchte aber dennoch die Einträge, die noch keinen Status 1 haben in eine Fehlertabelle schreiben.

Ich habe das jetzt in meinem Chaoscode wie folgt gelöst:

Code: Alles auswählen.

Loop at p_vbrk assigning field-symbol(<p_vbrk>). 
SELECT objky kschl erdat vstat parnr
           APPENDING CORRESPONDING FIELDS OF TABLE p_nast
           FROM nast
           WHERE objky EQ <p_vbrk>-vbeln AND
                 kschl EQ 'X'.
ENDLOOP. 

SORT p_nast. 

*Doppelte Einträge aus NAST Tabelle löschen. 

delete adjacent duplicates from p_nast comparing objky.

*Wenn der Status nicht gleich 1 ist, soll die Fehlertabelle gefüllt werden. 
LOOP AT p_nast ASSIGNING <p_nast>.
      IF <p_nast>-vstat NE 1.

        LOOP AT p_vbrk ASSIGNING <p_vbrk>
          WHERE vbeln EQ <p_nast>-objky.
          INSERT ZFI_FEHLER FROM <p_vbrk>.
        ENDLOOP.
      ENDIF.
    ENDLOOP.

*Ab hier nur werden nur noch die Einträge mit Status = 1 benötigt. 
DELETE p_nast WHERE vstat = '0' OR vstat = '2'.
Ich komme zwar damit zu meinem Ziel. Ich bin mir zwar nicht ganz sicher, ob bei Delete adjacent dublicates wirklich dann auch immer die Zeile mit dem Status 2 gelöscht wird und nicht plötzlich die mit Status 1. Deshalb habe ich das SORT vorweg gesetzt. Könnt ihr mir hierzu eure Einschätzung einmal sagen?

Nebenbei hatte ich mir eigentlich gedacht, dass ich die Einträge mit Status ungleich 1 eigentlich gar nicht selektieren muss, sondern direkt in eine Fehlertabelle schreibe, da ich sie ja sowieso anschließend wieder lösche. Ich wusste hier aber leider nicht so richtig, mit welchen Befehlen ich dies vielleicht lösen kann. Mich würde hier nur interessieren, habt ihr evtl. einen schöneren Ansatz, um meine Problematik abzubilden?

Danke euch.

Viele Grüße
Julia


Re: Doppelte Einträge aus einer internen Tabelle löschen anhand einer Bedingung

Beitrag von DeathAndPain (Top Expert / 1423 / 153 / 325 ) » 20.01.2020 14:00
Leider fehlt bei Deinem Beispielcode wieder das Wichtigste: Die Typ- und Datendefinitionen. 😢

Damit zusammenhängend ist die Frage, wofür Du diese internen Tabellen so alles brauchst. Davon hängt nämlich ab, welche Tabellenschlüssel Du ihnen verpassen solltest. In Deinen anderen Fragen hast Du immer nur mit Standardtabellen gearbeitet. Spätestens jetzt ist der Zeitpunkt gekommen, sich davon zu verabschieben und Tabellen des Typs SORTED oder gar HASHED zu verwenden.
Ich bin mir zwar nicht ganz sicher, ob bei Delete adjacent dublicates wirklich dann auch immer die Zeile mit dem Status 2 gelöscht wird und nicht plötzlich die mit Status 1.
Das hängt vom Tabellenschlüssel ab. Wenn Du keinen vergibst, vergibt SAP einen Defaultschlüssel, der im wesentlichen alle zeichenartigen Spalten der Tabelle von links nach rechts umfasst. Dein Status 2 wird auch Bestandteil dieses Schlüssels sein. KSCHL ist bei Dir immer 'X' (laut SELECT-Bedingung). Der Fall, dass die Zeile mit dem Status 1 gelöscht wird, kann dann und nur dann passieren, wenn es Zeilen mit demselben OBJKY gibt, bei denen der Status 1 ein größeres ERDAT hat, vorausgesetzt, dass die Reihenfolge der Spalten in Deiner Tabelle dieselbe ist wie in Deinem SELECT-Statement (was ich leider nicht weiß).

Aber selbst, wenn das nicht passieren kann, ist das sehr unschön gelöst. Ein passender, explizit deklarierter Schlüssel wäre besser. Wenn ich mit Standardtabellen arbeite, deklariere ich sie in aller Regel mit dem Zusatz WITH EMPTY KEY, um zu verdeutlichen, dass ich nicht an einer Arbeit mit dubios automatisch vom System erzeugten Defaultschlüsseln interessiert bin. Dann würde ein SORT wie der Deine auch meckern, weil Du ihm nicht gesagt hast, wonach er denn sortieren soll.

Außerdem deutet die Zeile

Code: Alles auswählen.

DELETE p_nast WHERE vstat = '0' OR vstat = '2'.
darauf hin, dass vstat = '0' auch passieren kann. Wenn es '0' und '1' gibt, fliegt '1' raus, logisch, denn der DELETE ADJACENT DUPLICATES behält immer die erste gefundene Zeile.

Ich verstehe sowieso nicht, wozu Du den DELETE ADJACENT DUPLICATES überhaupt brauchst. Du fragst ja im restlichen Programm den VSTAT ohnehin haarklein ab und schmeißt zum Schluss alle, die 0 oder 2 sind, raus.

Und warum eigentlich WHERE vstat = '0' OR vstat = '2', wenn Dein dortiger Kommentartext erklärt, dass Du eigentlich WHERE vstat <> '1' meinst?

Ein LOOP WHERE hat auch miserable Performance, wenn er auf einer Standardtabelle ausgeführt wird. Wenn Du solch LOOP brauchst, ist das ein klares Indiz dafür, dass Du die Tabelle als SORTED definieren solltest. Dann brauchst Du keinen SORT-Befehl mehr, und der LOOP sucht sich performant die benötigten Zeilen heraus (sofern Du in seiner WHERE-Bedingung die Schlüsselfelder der internen Tabelle in der richtigen Reihenfolge angibst und alle bis auf die letzte auf Gleichheit prüfst).

Re: Doppelte Einträge aus einer internen Tabelle löschen anhand einer Bedingung

Beitrag von Sonne1234 (ForumUser / 30 / 16 / 1 ) » 20.01.2020 14:54
Hallo DeathAndPain,

irgendwie denke ich immer, dass Deklarationen nicht so wichtig sind und dann sind immer genau diese der entscheidende Punkt. Ich versuche diese ab sofort immer mitzuliefern.

Code: Alles auswählen.

types: BEGIN OF ty_vbrk,
         vbeln TYPE vbrk-vbeln,
         fkart TYPE vbrk-fkart,
         fkdat TYPE vbrk-fkdat,
         rfbsk TYPE vbrk-rfbsk,
         END OF ty_vbrk,

        BEGIN OF ty_nast,
         objky      TYPE nast-objky,
         kschl      TYPE nast-kschl,
         erdat      TYPE nast-erdat,
         vstat      TYPE nast-vstat,
         END OF ty_nast.

TYPES: tty_vbrk  TYPE TABLE OF ty_vbrk,
       tty_nast  TYPE TABLE OF ty_nast.

data: gt_nast TYPE tty_nast,
        gt_vbrk type tty_vbkr. 

PERFORM f_nast USING gt_vbrk CHANGING gt_nast.

Loop at p_vbrk assigning field-symbol(<p_vbrk>). 

SELECT objky kschl erdat vstat parnr
           APPENDING CORRESPONDING FIELDS OF TABLE p_nast
           FROM nast
           WHERE objky EQ <p_vbrk>-vbeln AND
                 kschl EQ 'X'.
ENDLOOP. 

SORT p_nast. 

*Doppelte Einträge aus NAST Tabelle löschen. 

delete adjacent duplicates from p_nast comparing objky.

*Wenn der Status nicht gleich 1 ist, soll die Fehlertabelle gefüllt werden. 
LOOP AT p_nast ASSIGNING <p_nast>.
      IF <p_nast>-vstat NE 1.

        LOOP AT p_vbrk ASSIGNING <p_vbrk>
          WHERE vbeln EQ <p_nast>-objky.
          INSERT ZFI_FEHLER FROM <p_vbrk>.
        ENDLOOP.
      ENDIF.
    ENDLOOP.

*Ab hier nur werden nur noch die Einträge mit Status = 1 benötigt. 
DELETE p_nast WHERE vstat = '0' OR vstat = '2'.
So ich hoffe, ich habe jetzt keine Deklaration vergessen.

Also den delete adjacent duplicates brauche ich aus meiner Sicht, weil ich die Einträge nicht doppelt in meine Fehlertabelle schreiben möchte.

Also die Tabelle p_nast hat z. B. die folgenden Einträge:

900564546 X 17.06.19 2
900564546 X 18.06.19 1
900564212 X 17.06.19 1
900651574 X 18.06.19 2

Nehmen wir mal an, ich lasse das Programm am 18.06.19 laufen, dann möchte ich, dass in meine Fehlertabelle die folgenden Einträge erfasst werden.

900651574 ZZZZ 18.06.19 (Diese Informationen sollen aus der VBRK kommen und nicht aus der NAST.)

Der Eintrag 900564546 soll aber nicht mehr in der Fehlertabelle erscheinen, da dieser ja bereits einen zweiten Eintrag mit dem Status 1 bekommen hat und somit erledigt ist. Deshalb habe ich versucht den delete adjacent duplicates anzuwenden. Aber wie du schon bemerkt hast, gibt es auch Einträge mit 0 und die sollten ja eigentlich auch gelöscht werden, wenn es einen Eintrag mit 1 gibt.
Denn wenn ich diese Einträge nicht lösche, hatte ich in meiner Fehlertabelle das Problem, dass dort zwar keine Belege mit dem Status 1 erfasst wurden, aber Belege mit dem Status 2, die eigentlich abgeschlossen sind, das sie gleichzeitig noch einen Eintrag mit dem Status 1 haben. Ich hoffe, es wird deutlich, wo hier mein Problem ist.

Und ja warum Delete p_nast where vstat = '0'. or vstat = '2' - weiß ich auch nicht. Es führt doch auch zum Ziel, oder nicht? ;)

Und danke für deine Geduld und ausführlichen Antworten :)

Re: Doppelte Einträge aus einer internen Tabelle löschen anhand einer Bedingung

Beitrag von schick (ForumUser / 52 / 5 / 14 ) » 20.01.2020 14:57
Hi,

wenn ich die Aufgabe richtig verstehe, geht es darum fehlerhafte Nachrichten zu finden, bzw. eine Liste aller Belege zu erstellen zu denen es keine erfolgreich verarbeitete Nachricht gibt. Ist das soweit richtig verstanden?
Ich persönlich würde das Ganze mit einem Select mit Sub-Query lösen denke ich, ob das die eleganteste Methode ist möchte ich aber nicht bewerten.

Könnte in etwa so gehen:
Achtung nicht getestet da grad kein System greifbar.

Code: Alles auswählen.

SELECT objky kschl erdat vstat parnr
FROM nast
INTO CORRESPONDING FIELDS OF TABLE p_table
WHERE objky EQ <p_vbrk>-vbeln 
AND kschl EQ 'X'.
AND NOT EXISTS ( 
SELECT vstat 
FROM nast 
WHERE objky EQ <p_vbrk>-vbeln 
AND kschl EQ 'X'
AND vstat EQ '1' ).

Re: Doppelte Einträge aus einer internen Tabelle löschen anhand einer Bedingung

Beitrag von LostDarkness (ForumUser / 79 / 14 / 6 ) » 20.01.2020 15:43
DeathAndPain hat geschrieben:
20.01.2020 14:00
Spätestens jetzt ist der Zeitpunkt gekommen, sich davon zu verabschieben und Tabellen des Typs SORTED oder gar HASHED zu verwenden.
Melde mich mal so halb Off-Topic dazu:

Kann hier jemand ein gutes Tutorial empfehlen um sich damit den verschiedenen Tabellentypen auseinander zu setzen?
Habe am Wochenende begonnen mich damit auseinander zu setzen und die F1-Hilfe ist mir nicht ganz schlüssig genug, insbesondere fehlen mir dort praktische Beispiele. Kennt da jemand was? :)

Beste Grüße
Gerrit
“You should name a variable using the same care with which you name a first-born child.”
― Robert C. Martin

Re: Doppelte Einträge aus einer internen Tabelle löschen anhand einer Bedingung

Beitrag von DeathAndPain (Top Expert / 1423 / 153 / 325 ) » 20.01.2020 16:05
@schick: Sie will ja nicht nur die Fehlerliste, sondern auch die Nutzdaten, um damit weiterzuarbeiten.

@Sonne: Es fehlen die DATA-Deklarationen von p_vbrk und p_nast. Zwar ist zu vermuten, dass diese die von Dir angegebenen Typen tty_vbrk und tty_nast verwenden, aber dann stellt sich die Frage, weshalb Du in Deinem SELECT auch die Spalte PARNR liest, obgleich tty_nast überhaupt kein Feld dafür hat. Außerdem ist in Deinem SELECT kein LIKE drin, weswegen es eigentlich möglich sein sollte, Dir den LOOP zu sparen und performanter mit FOR ALL ENTRIES IN zu arbeiten.

Außerdem halte ich es für problematisch, dass Du zuerst über p_vbrk loopst, um p_nast zu füllen und anschließend dann über p_nast, wobei Du mit den Werten wieder p_vbrk liest. Sollte es zu einem p_vbrk aus irgendeinem Grund - und sei es eine Dateninkonsistenz auf der Datenbank - gar keinen Eintrag in der nast geben, dann führt diese Vorgehensweise dazu, dass die entsprechende p_vbrk-Zeile in Deiner Ergebnistabelle komplett fehlt!

Stattdessen würde ich an Deiner Stelle sagen, Deine Ausgangstabelle ist p_vbrk, und zu deren Sätzen willst Du Informationen beschaffen. Daher würde ich immer von dort aus lesen.

Hier ist ein Vorschlag, wie Du Dein Ziel erreichen kannst, unter der Maßgabe, dass ich Deine Anforderungen richtig verstanden habe:

Code: Alles auswählen.

types: BEGIN OF ty_vbrk,
         vbeln TYPE vbrk-vbeln,
         fkart TYPE vbrk-fkart,
         fkdat TYPE vbrk-fkdat,
         rfbsk TYPE vbrk-rfbsk,
         END OF ty_vbrk,

        BEGIN OF ty_nast,
         objky      TYPE nast-objky,
         kschl      TYPE nast-kschl,
         erdat      TYPE nast-erdat,
         vstat      TYPE nast-vstat,
         END OF ty_nast.

TYPES: tty_vbrk  TYPE TABLE OF ty_vbrk,
       tty_unsorted_nast TYPE STANDARD TABLE OF ty_nast WITH EMPTY KEY,
       tty_nast  TYPE SORTED TABLE OF ty_nast WITH NON-UNIQUE KEY objky vstat.

data: gt_nast TYPE tty_nast,
        gt_vbrk type tty_vbrk,
        gt_unsorted_nast TYPE tty_unsorted_nast,
        p_nast TYPE tty_nast.

PERFORM f_nast USING gt_vbrk CHANGING gt_nast.

Loop at p_vbrk assigning field-symbol(<p_vbrk>). 

SELECT objky kschl erdat vstat parnr
           APPENDING CORRESPONDING FIELDS OF TABLE gt_unsorted_nast
           FROM nast
           WHERE objky EQ <p_vbrk>-vbeln AND
                 kschl EQ 'X'.
ENDLOOP. 

p_nast[] = gt_unsorted_nast[]. " aus unsortierter in sortierte Tabelle übertragen - das System sortiert dabei automatisch passend
FREE gt_unsorted_nast.

* Wenn es zu einem Beleg keinen Eintrag mit Status 1 gibt, soll die Fehlertabelle gefüllt werden. 
LOOP AT p_vbrk assigning <p_vbrk>.
  CHECK NOT LINE_EXISTS( p_nast[ objky = <p_vbrk>-VBELN vstat = '1' ] ).
  INSERT ZFI_FEHLER FROM <p_vbrk>.
ENDLOOP

*Ab hier nur werden nur noch die Einträge mit Status = 1 benötigt. 
DELETE p_nast WHERE vstat <> '1'.
Dabei bin ich davon ausgegangen, dass Du Gründe hast, ohne FOR ALL ENTRIES IN zu arbeiten (denn das wäre viel schneller und eleganter). Dann brauchst Du den APPENDING TABLE, weswegen Deine Zieltabelle dann nicht SORTED sein kann. Du brauchst sie aber SORTED, da Du sonst nicht effizient darin suchen kannst. Aus diesem Grunde nutze ich hilfsweise eine unsortierte Hilfstabelle, um die Daten darin zusammenzulesen, übertrage den Inhalt dann aber gleich im Anschluss in eine nach Deinen Bedürfnissen sortiert definierte Tabelle (beachte meine geänderte Definition von tty_nast!) und gebe den Speicher der unsortierten Hilfstabelle mit FREE wieder frei. (Den Befehl nutzt kaum einer, aber neben der Speicherersparnis ist er auch für den Leser gut, dem Du damit signalisierst, dass Du diese Tabelle im weiteren Programmverlauf nicht mehr brauchst.)

Dann loope ich wieder über die führende Tabelle p_vbrk und gebe alle Zeilen aus, zu denen es in p_nast keine Entsprechung mit Status 1 gibt. Hierzu nutze ich den Index der sortierten Tabelle p_nast in Verbindung mit dem Konstrukt LINE_EXISTS, das sehr schnell ist. Damit erwische ich auch etwaige Belege, zu denen es in p_nast überhaupt nichts gibt (nach meinem Verständnis wäre das aus Deiner Sicht eine Inkonsistenz und damit auch ein Fehlerfall).

Da Du offenbar hinterher noch Pläne mit den p_nast-Zeilen der korrekt verarbeiteten Belege hast, mache ich dann noch den DELETE WHERE, um den Rest rauszuschmeißen. Fertig.
Und ja warum Delete p_nast where vstat = '0'. or vstat = '2' - weiß ich auch nicht. Es führt doch auch zum Ziel, oder nicht? ;)
Ja, aber:
  • Es ist länger.
  • Es ist weniger performant (da zwei Prüfungen anstatt einer ausgeführt werden).
  • Es ist verwirrend, da Du im Kommentar genau schreibst, was Du machen willst, Dein Code dann aber - ohne Not - etwas anderes macht und der Leser erst darüber nachdenken musst, ob beides logisch äquivalent ist oder nicht
Schlimm ist es trotzdem nicht. Ich wollte es nur erwähnen. Code soll ja das, was Du im Geiste machen möchtest, in Maschinenbefehle gießen. Wenn Du schon explizit für den Leser niederschreibst, was Du tun möchtest, sollte der Code optimalerweise genau das widerspiegeln.

Seite 1 von 1

Über diesen Beitrag


Unterstütze die Community und teile den Beitrag für mehr Leser und besseren Inhalt:

Vergleichbare Themen

doppelte Einträge löschen Distinct über mehrere Spalten
von tmxx » 03.03.2008 13:53
Doppelte Datensätze aus einer Tabelle löschen
von managero » 21.04.2008 17:34
Maximale Anzahl der Einträge in einem internen Tabelle
von msentaburlar » 23.02.2020 01:12
doppelte einträge
von zwiback » 28.10.2005 08:52
doppelte Einträge
von Charly » 27.05.2003 08:09