Das Muster, das ABAP-Routinen am stärksten prägt: eine Schleife über das Paket mit einem READ TABLE auf eine Hilfstabelle, die vorher aus demselben Paket aufgebaut wurde. Typisch für Anteile, Summen je Beleg oder den Vergleich mit einem Nachbarsatz. In SQLScript wird daraus eine Kette aus Tabellenvariablen.

LOOP und READ TABLE

Die ABAP-Routine

Der Anteil einer Position am Belegwert. Erste Schleife: Summen je Beleg sammeln. Zweite Schleife: jeden Satz mit seiner Summe verrechnen.

METHOD end_routine.

  TYPES: BEGIN OF ty_total,
           doc_number TYPE /bic/oizdocnum,
           amount     TYPE /bic/oizamount,
         END OF ty_total.
  DATA lt_total TYPE SORTED TABLE OF ty_total
                WITH UNIQUE KEY doc_number.
  DATA ls_total TYPE ty_total.

  LOOP AT result_package INTO DATA(ls_result).
    ls_total-doc_number = ls_result-doc_number.
    ls_total-amount     = ls_result-amount.
    COLLECT ls_total INTO lt_total.
  ENDLOOP.

  LOOP AT result_package ASSIGNING FIELD-SYMBOL(<ls_result>).
    READ TABLE lt_total INTO ls_total
         WITH KEY doc_number = <ls_result>-doc_number.
    IF sy-subrc = 0 AND ls_total-amount <> 0.
      <ls_result>-amount_share = <ls_result>-amount / ls_total-amount.
    ENDIF.
  ENDLOOP.

ENDMETHOD.

Die SQLScript-Fassung

Die erste Schleife ist ein GROUP BY. Die zweite ist ein Join des Pakets mit seinen eigenen Summen; ein Join zwischen zwei Tabellenvariablen funktioniert wie mit einer Datenbanktabelle, nur mit Doppelpunkt und ohne USING. Die Division kommt in einen dritten Schritt, weil ein berechnetes Feld im selben SELECT nicht für eine weitere Berechnung verwendet werden darf. Die Prüfung auf null vor der Division ersetzt die Bedingung in ABAP.

lt_total = SELECT doc_number,
                  SUM( amount ) AS total_amount
             FROM :inTab
            GROUP BY doc_number;

lt_joined = SELECT src.doc_number,
                   src.doc_item,
                   src.amount,
                   tot.total_amount,
                   src.record,
                   src.sql__procedure__source__record
              FROM :inTab AS src
              LEFT OUTER JOIN :lt_total AS tot
                ON tot.doc_number = src.doc_number;

outTab = SELECT doc_number,
                doc_item,
                amount,
                CASE
                  WHEN total_amount <> 0 THEN amount / total_amount
                  ELSE 0
                END AS amount_share,
                record,
                sql__procedure__source__record
           FROM :lt_joined;

Kompakter ist eine korrelierte Unterabfrage in der Feldliste. Bei großen Paketen ist GROUP BY mit Join im Vorteil, weil die Summen einmal gebildet werden und nicht einmal je Satz.

( SELECT SUM( t.amount )
    FROM :inTab AS t
   WHERE t.doc_number = src.doc_number ) AS total_amount

Ein LOOP mit AT NEW, AT END OF oder dem Merken des vorherigen Satzes hat ebenfalls ein Gegenstück: Fensterfunktionen. LAG( amount ) OVER ( PARTITION BY doc_number ORDER BY doc_item ) liefert den Betrag der vorherigen Position, LEAD() den der nächsten, SUM( amount ) OVER ( PARTITION BY doc_number ) die Belegsumme ohne eigenen GROUP-BY-Schritt.