*---------------------------------------------------------------------*
*                  Z _ B J H _ I N N E R _ J O I N                    *
*                                                                     *
*                 Autor: Bernd Haase (BJH Software)                   *
*---------------------------------------------------------------------*
* Einlesemöglichkeiten von externen SAP-Tabellen in interne Tabellen. *
* Zugriffe auf Tabelle, welche über Fremdschlüssel verknüpft sind.    *
* Vorstellung unterschiedlicher Methodiken, dessen Einsatz im Fall zu *
* Fall zu entscheiden ist unter den Gesichtspunkten zu erwartende     *
* Ergebnismenge, Performance, Komplexittät.                           *
*                                                                     *
* 1.) Konventionel: geschachtelte Select-Struktur.                    *
* 2.) Hashed: Blockweises Lesen der EKKO-Tabelle und mischen          *
* 3.) Join: allerdings satzweises Lesen und fortschreiben             *
* 4.) Join: auffüllen und einlesen in eine interne Tabelle            *
* 5.) Collect: Fremdschlüsselbeziehungen auffüllen                    *
* 6.) Mega-Join: 7 Tabellen gleichzeitig verknüpft. Nur wenn die Werte*
* in allen Tabellen zugeordnet werden konnten, ist der Datensatz      *
* gültig. Initialwerte führen zur Ablehnung des Datensatzes !         *
*---------------------------------------------------------------------*
* 20.07.2004 BHaa: Programm erstellt                                  *
* 28.08.2004 BHaa: erweitert um Hashed und Collect Methodik           *
*---------------------------------------------------------------------*
REPORT  z_bjh_inner_join           .

TABLES:
 ekko,    "Einkaufsbelegkopf
 ekpo,    "Einkaufsbelegposition
 lfa1,    "Lieferantenstamm (allgemeiner Teil)
 mara,    "Allgemeine Materialdaten
 makt,    "Materialkurztexte
 marc,    "Werksdaten zum Material
 t005,    "Länder
 t024d.   "Materialdisponenten

DATA:
 anzahl(6) TYPE n.

DATA:
**************
* Bestellung *
**************
 wa_ekko LIKE ekko,
 wa_ekpo LIKE ekpo,

* Struktur und interne Tabelle für den Zugriff auf die Bestellkopfdaten
 BEGIN OF wk_ekko,
   ebeln LIKE ekko-ebeln,
   lifnr LIKE ekko-lifnr,
 END OF wk_ekko,
 wktab_ekko LIKE HASHED TABLE OF wk_ekko WITH UNIQUE KEY ebeln,

* Struktur und interne Tabelle für den Inner-Join, abgekürzt durch "ij"
 BEGIN OF wk_ij,
   ebeln LIKE ekko-ebeln,
   ebelp LIKE ekpo-ebelp,
   lifnr LIKE ekko-lifnr,
   matnr LIKE ekpo-matnr,
 END OF wk_ij,
 wktab_ij LIKE TABLE OF wk_ij.

*************
* Lieferant *
*************
DATA:
 BEGIN OF ct_lfa1,
   lifnr LIKE lfa1-lifnr,
 END OF ct_lfa1,
 cttab_lfa1 LIKE TABLE OF ct_lfa1,

 BEGIN OF wk_lfa1,
   lifnr LIKE lfa1-lifnr,
   name1 LIKE lfa1-name1,
   land1 LIKE lfa1-land1,
 END OF wk_lfa1,
 wktab_lfa1 LIKE HASHED TABLE OF wk_lfa1 WITH UNIQUE KEY lifnr.

************
* Material *
************
DATA:
 BEGIN OF ct_mara,
   matnr LIKE mara-matnr,
 END OF ct_mara,
 cttab_mara LIKE TABLE OF ct_mara,

 BEGIN OF wk_mara,
   matnr LIKE mara-matnr,
   maktx LIKE makt-maktx,    "Kurztext zum Material
 END OF wk_mara,
 wktab_mara LIKE HASHED TABLE OF wk_mara WITH UNIQUE KEY matnr.

* Struktur und interne Tabelle für den Mega-Inner-Join über 7 Datenbank-
* tabellen
DATA:
 BEGIN OF mega_ij,
   ebeln LIKE ekko-ebeln,    "Bestellnummer
   ebelp LIKE ekpo-ebelp,    "Bestellposition
   lifnr LIKE ekko-lifnr,    "Lieferant
   intca LIKE t005-intca,    "ISO-Code des Landes
   matnr LIKE ekpo-matnr,    "Material
   werks LIKE ekpo-werks,    "Werk, in welches geliefert werden soll
   dsnam LIKE t024d-dsnam,   "Name des Disponenten
   maktx LIKE makt-maktx,    "Kurztext zum Material
 END OF mega_ij,

 megatab_ij LIKE TABLE OF mega_ij.

*---------------------------------------------------------------------*
*                                                                     *
*                 A u s w a h l b i l d s c h i r m                   *
*                                                                     *
*---------------------------------------------------------------------*
SELECT-OPTIONS:
 so_lifnr FOR ekko-lifnr,    "Lieferantennummer
 so_matnr FOR ekpo-matnr.    "Materialnummer

INITIALIZATION.
 CLEAR: so_lifnr. REFRESH: so_lifnr.

* Vorbesetzung eines Lieferanten, um die Ergebnismenge einzugrenzen
 MOVE 'I'      TO so_lifnr-sign.
 MOVE 'EQ'     TO so_lifnr-option.
 MOVE '205670' TO so_lifnr-low.
 APPEND so_lifnr.

START-OF-SELECTION.
 MOVE 0 TO anzahl.

********************
* 1. Konventionell *
********************
* Konventionelle Methodik zum Auslesen von Einkufskopfadten und deren
* Positionen
 CLEAR: wktab_ij, anzahl.

 SELECT * FROM ekko INTO wa_ekko
 WHERE lifnr IN so_lifnr.
   SELECT * FROM ekpo INTO wa_ekpo
   WHERE ebeln = wa_ekko-ebeln
   AND   matnr IN so_matnr.
     MOVE-CORRESPONDING wa_ekko TO wk_ij.
     MOVE-CORRESPONDING wa_ekpo TO wk_ij.
     APPEND wk_ij TO wktab_ij.

*      WRITE: / wa_ekko-ebeln, wa_ekpo-ebelp, wa_ekko-lifnr,
*               wa_ekpo-matnr.
     ADD 1 TO anzahl.
   ENDSELECT.
 ENDSELECT.

* Statistik. Ergebnis anzeigen
 IF sy-subrc = 0.
*    SKIP 1.
   WRITE AT: /1 'Select * into wa', 30 anzahl, 37 'Datensätze'.
 ELSE.
   WRITE: / 'Keine Daten gefunden.'.
 ENDIF.

 SKIP 1.

*************
* 2. Hashed *
*************
 CLEAR: wktab_ij, anzahl.

* einmaliges EInlesen aller betroffenen Bestellungen
 SELECT ebeln lifnr FROM ekko
 INTO TABLE wktab_ekko
 WHERE lifnr IN so_lifnr.

* einzelweises Nachlesen der Positionen, denen dann aber die Kopfdaten
* beimischen dabei bezieht sich die Auswahlmenge der einzulesenden Daten
* der Positionen jedoch schon auf die Wertemenge (Ergebnistabelle) der
* gefundenen Kopfdaten
 SELECT * FROM ekpo INTO wa_ekpo
 FOR ALL ENTRIES IN wktab_ekko
 WHERE ebeln = wktab_ekko-ebeln
 AND   matnr IN so_matnr.
* Positionsatz. Nun prüfen, ob es zu diesem auch einen Kopfsatz gibt.
* Die Auswahlmenge wurde bereits beim Einlesen reduziert
   READ TABLE wktab_ekko INTO wk_ekko
   WITH TABLE KEY ebeln = wa_ekpo-ebeln.
   IF sy-subrc = 0.
     MOVE-CORRESPONDING wa_ekko TO wk_ij.
     MOVE-CORRESPONDING wa_ekpo TO wk_ij.
     APPEND wk_ij TO wktab_ij.

*      WRITE: / wa_ekko-ebeln, wa_ekpo-ebelp, wa_ekko-lifnr,
*               wa_ekpo-matnr.
     ADD 1 TO anzahl.
   ENDIF.
 ENDSELECT.

* Statistik. Ergebnis anzeigen
 IF sy-subrc = 0.
*    SKIP 1.
   WRITE AT: /1 'Select und Hashed', 30 anzahl, 37 'Datensätze'.
 ELSE.
   WRITE: / 'Keine Daten gefunden.'.
 ENDIF.

 SKIP 1.

***************************
* 3. Inner Join satzweise *
***************************
* Inner-Join-Methodik mit satzweisem Einlesen
 MOVE 0 TO anzahl.

 SELECT ij_ekko~ebeln ij_ekpo~ebelp ij_ekko~lifnr ij_ekpo~matnr
 FROM       ekko AS ij_ekko
 INNER JOIN ekpo AS ij_ekpo
 ON    ij_ekko~ebeln = ij_ekpo~ebeln
 INTO   (wa_ekko-ebeln, wa_ekpo-ebelp, wa_ekko-lifnr, wa_ekpo-matnr)
 WHERE ij_ekko~lifnr IN so_lifnr
 AND   ij_ekpo~matnr IN so_matnr.
*    WRITE: / wa_ekko-ebeln, wa_ekpo-ebelp, wa_ekko-lifnr,
*             wa_ekpo-matnr.
   ADD 1 TO anzahl.
 ENDSELECT.

 IF sy-subrc = 0.
*    SKIP 1.
   WRITE AT: /1 'Inner Join into wa', 30 anzahl, 37 'Datensätze'.
 ELSE.
   WRITE: / 'Keine Daten gefunden.'.
 ENDIF.

*******************************
* 4. Inner Join tabellenweise *
*******************************
* Inner-Join-Methodik mit tabellenweisem Einlesen
 SELECT *
 FROM       ekko AS ij_ekko
 INNER JOIN ekpo AS ij_ekpo
 ON    ij_ekko~ebeln = ij_ekpo~ebeln
 INTO CORRESPONDING FIELDS OF TABLE wktab_ij
 WHERE ij_ekko~lifnr IN so_lifnr
 AND   ij_ekpo~matnr IN so_matnr.

 DESCRIBE TABLE wktab_ij LINES anzahl.

 SKIP 1.
 WRITE AT: /1 'Inner Join into Table', 30 anzahl, 37 'Datensätze'.

**************
* 5. Collect *
**************
* eignet sich, wenn von einer Tabelle zusätzliche Daten aus weiteren
* Tabellen eingelesen werden sollen, welche von den Ursprungsdaten über
* Fremdschlüsselbeziehungen erreichbar sind.
* Die Collect-Methodik ist eigentlich zum Aufaddieren für Gruppensummen
* entwickelt worden, eignet sich aber für die Fremdschlüsselbeziehungen
 CLEAR: megatab_ij, cttab_lfa1, cttab_mara, wktab_lfa1, wktab_mara,
        anzahl.

* Ausgangstabelle ist die bereits unter Punkt 4. erzeugte Ergebnismenge
 LOOP AT wktab_ij INTO wk_ij.
   MOVE wk_ij-lifnr TO ct_lfa1-lifnr.
   COLLECT ct_lfa1 INTO cttab_lfa1.

   MOVE wk_ij-matnr TO ct_mara-matnr.
   COLLECT ct_mara INTO cttab_mara.
 ENDLOOP.

* lieber mit Abfrage, denn bei leere Ergebnismenge kann das System davon
* ausgehen, alle Werte der gewünschten Tabelle einzulesen
 IF NOT cttab_lfa1 IS INITIAL.
   SELECT * FROM lfa1
   INTO CORRESPONDING FIELDS OF TABLE wktab_lfa1
   FOR ALL ENTRIES IN cttab_lfa1
   WHERE lifnr = cttab_lfa1-lifnr.
 ENDIF.

 IF NOT cttab_mara IS INITIAL.
* Inner-Join-Methodik zum Einelsen der Materialen und deren Texte
   SELECT *
   FROM       mara AS ij_mara
   INNER JOIN makt AS ij_makt
   ON    ij_mara~matnr = ij_makt~matnr
   INTO CORRESPONDING FIELDS OF TABLE wktab_mara
   FOR ALL ENTRIES IN cttab_mara
   WHERE ij_mara~matnr = cttab_mara-matnr
   AND   ij_makt~spras = sy-langu.
 ENDIF.

* erneuter Lauf der Ausgangstabelle, da jetzt aber alle zusätzlich
* benötigten Daten zur Verfügung stehen, kann gemischt werden und die
* Endergebnistabelle befüllt werden. Sollten für die Fremdschlüssel-
* beziehung keine zusätzlichen Daten existieren, so werden diese Felder
* frei gelassen. Reihenfolge beachten !
 LOOP AT wktab_ij INTO wk_ij.
   READ TABLE wktab_lfa1 INTO wk_lfa1
   WITH TABLE KEY lifnr = wk_ij-lifnr.
   IF sy-subrc <> 0.
     CLEAR: wk_lfa1.
   ENDIF.

   READ TABLE wktab_mara INTO wk_mara
   WITH TABLE KEY matnr = wk_ij-matnr.
   IF sy-subrc <> 0.
     CLEAR: wk_mara.
   ENDIF.

   MOVE-CORRESPONDING wk_lfa1 TO mega_ij.
   MOVE-CORRESPONDING wk_mara TO mega_ij.
   MOVE-CORRESPONDING wk_ij   TO mega_ij.
   APPEND mega_ij TO megatab_ij.
 ENDLOOP.

 DESCRIBE TABLE megatab_ij LINES anzahl.

 SKIP 1.
 WRITE AT: /1 'Collect Methodik', 30 anzahl, 37 'Datensätze'.

****************
* 6. Mega-Join *
****************
* auch so könnte man die Punkte 4. und 5. realisieren. Die Wertemenge
* ist aber eingeschränkt, da keine Outer-Join Methodik

* Mega-Join über 7 Tabellen. Dauert ein wenig, bevor das Ergebnis fest-
* steht. Allerdings ist Nachteil, das sollte die Materialnummer fehlen
* (Nichtlagersätze, Dienstleistungen), der gesamte Datensatz ignoriert
* wird, da kein erfolgreicher Zugriff auf die Materialtabelle durchge-
* führt werden konnte. Sollten Felder mit gleichem Namen in mehreren
* Tabellen vorkommen, Übertrag und Füllung interne Tabelle mega_ij
* beachten (Beispiel WERKS exisitert in den Tabellen EKPO; LFA1, MARC
* und T024D).
 SELECT *
 FROM       ekko AS ij_ekko
 INNER JOIN ekpo AS ij_ekpo
 ON    ij_ekko~ebeln = ij_ekpo~ebeln
 INNER JOIN lfa1 AS ij_lfa1
 ON    ij_ekko~lifnr = ij_lfa1~lifnr
 INNER JOIN marc AS ij_marc
 ON    ij_ekpo~matnr = ij_marc~matnr
 AND   ij_ekpo~werks = ij_marc~werks
 INNER JOIN makt AS ij_makt
 ON    ij_ekpo~matnr = ij_makt~matnr
 INNER JOIN t005 AS ij_t005
 ON    ij_lfa1~land1 = ij_t005~land1
 INNER JOIN t024d AS ij_t024d
 ON    ij_ekpo~werks = ij_t024d~werks
 AND   ij_marc~dispo = ij_t024d~dispo
 INTO CORRESPONDING FIELDS OF TABLE megatab_ij
 WHERE ij_ekko~lifnr IN so_lifnr
 AND   ij_ekpo~matnr IN so_matnr
 AND   ij_makt~spras =  sy-langu.

 DESCRIBE TABLE megatab_ij LINES anzahl.

 SKIP 1.
 WRITE AT: /1 'Mega Join into Table', 30 anzahl, 37 'Datensätze'.

* Ergebnisanzeige für den Mega-Join, jede gefundene Zeile
 LOOP AT megatab_ij INTO mega_ij.
   WRITE: / mega_ij-ebeln, mega_ij-ebelp,
            mega_ij-lifnr, mega_ij-intca,
            mega_ij-matnr, mega_ij-werks,
            mega_ij-maktx(15), mega_ij-dsnam.
 ENDLOOP.

END-OF-SELECTION.
 EXIT.

***** Ende *****
* Copyright BJH Software, Datei überarbeitet am: 21.5.2005