Sie sind nun wieder eingeladen, die Diskussion spezieller technischer Probleme mit zu verfolgen. Bitte schicken Sie Fragen, Anregungen oder Antworten zu den vorgestellten Themen – ebenso wie Ihre Kritik – an unsere e-Mail-Adressen: dieter.bender@MidrangeMagazin.de oder Redaktion@MidrangeMagazin.de
Prozedur-Aufrufe in einer Schleife
Frage: Ich habe in einem RPG-Programm mehrere gleichartige Aufrufe, die nacheinander abgearbeitet werden müssen. Mit dynamischen CALL-Befehlen kann ich die Programmnamen in ein Array schreiben und dann in einer Schleife Programm für Programm aufrufen. Geht das auch mit gebundenen Calls oder mit Prozedur-Aufrufen? Ich habe bisher bei allen Versuchen Fehler bei der Umwandlung bekommen.
Antwort: Diese Aufgabenstellung ist in der Tat bei gebundenen Aufrufen etwas kniffliger als beim dynamischen Aufruf. Man darf als Programmname bei CALLB keine Variablen verwenden, da der Compiler ja zur Bindezeit die Programmnamen zum Binden braucht. Man könnte natürlich über MI-Funktionen und API-Aufrufe voll dynamisch zur Laufzeit Prozeduren binden, aber die Anforderung ist bereits einfacher zu erfüllen. Der entscheidende Kniff besteht darin, die Prozeduren in einem Array von Procedure Pointern zu verwalten.
Die Deklaration dieses Arrays erfolgt so wie gewöhnlich mit dem Schlüsselwort DIM.
Für Procedure Pointer wird als interne Datenart mit einem * der Typ „Pointer“ deklariert und mit dem Schlüsselwort PROCPTR der spezifische Typ festgelegt. Die Initialisierung der Procedure Pointer erfolgt dann mit Hilfe der Built-in Function %PADDR (siehe Listing 1).
Im Free Format RPG darf man das ohnehin überflüssige EVAL weglassen, dafür muss man dann das ebenfalls nicht sehr sinnvolle Semikolon am Schluss hinschreiben. Vielleicht sollte man in dieser Rubrik mal einen kleinen PreCompiler bringen, der das Free Format RPG abrundet. Diese Procedure Pointer könnten jetzt schon direkt zum Aufruf mit CALLB verwendet werden (siehe Listing 2).
Der Aufruf mit CALLB kann allerdings nicht im Free Format erfolgen und erfordert dann gegebenenfalls einen Wechsel des Formates.
Im Free Format wird hier auf den Operations Code CALLP verwiesen, der jedoch keine Verwendung eines Procedure Pointers erlaubt. Mit einem kleinen Trick lässt sich diese Hürde jedoch überspringen. Dazu deklariert man zuerst einen Prototyp für den Aufruf und verbindet diesen mit einem zugehörigen Procedure Pointer. Selbstverständlich muss dieser Prototyp zu den Prototypen der Prozeduren passen, die über das Array von Procedure Pointern aufgerufen werden sollen (siehe Listing 3).
Der Eintrag mit dem Schlüsselwort EXTPROC(procP) koppelt diesen Prototyp an einen Procedure Pointer procP, der als zweite Deklaration zu sehen ist. Dieser Prototyp wird dann in der Schleife für den CALLP verwendet. Der Operator CALLP kann im Free Format wegfallen, und ab Version 5 darf man auch eine leere Klammer für den Aufruf von Prozeduren ohne Parameter hinschreiben, was der Lesbarkeit zu Gute kommt (siehe Listing 4).
In der Schleife wird jetzt der Procedure Pointer des Aufruf-Prototyps nacheinander mit den Werten aus dem Array belegt und anschließend wird der zugehörige Prototyp für den Aufruf benutzt. Selbstverständlich können mit dieser Technik auch Prozeduren mit Rückgabewerten und Übergabeparametern verwendet werden, die selbstredend dann alle über denselben Prototyp aufrufbar sein müssen – also verträgliche Parameter-Strukturen haben müssen.
Zeilennummerierung mit SQL
Frage: Ist es möglich, mittels SQL eine Zeilennummerierung beim Füllen einer Tabelle einzubringen? Beispiel: Eine Tabelle wird über einen Select mit 5 Datensätzen gefüllt. In ein speziell angelegtes numerisches Feld der Tabelle soll dann pro Zeile die laufende Zeilennummer eingetragen werden.
Antwort: Es gibt zwei einfache Ansatzpunkte in SQL zur Lösung dieses Problems. Der erste Weg besteht darin, das Feld mit der Nummerierung zunächst nicht zu belegen und dann in einem zweiten Schritt die relative Satznummer in das entsprechende Feld zu übernehmen.
Hierzu wird dann mit einer zweiten SQL Anweisung das Update durchgeführt (siehe Listing 5).
Dieses Verfahren hat natürlich den Nachteil, dass man nicht weitergehend steuern kann, was in das Feld eingestellt wird. Zudem braucht man zwei Anweisungen. Bei der Wiederverwendung gelöschter Sätze, wie das für SQL erstellte Dateien per Unterlassungswert der Fall ist, ist der gewünschte Zweck womöglich nicht erreichbar. Wesentlich flexibler ist da der alternative Weg mit der Erzeugung einer Nummerierung durch Zählen der kleineren Werte. Am besten ist dies wieder an einem Beispiel zu sehen (siehe Listing 6).
In dem kleinen Beispiel werden aus einer Datei KUNDE alle Kunden mit dem Namen Müller selektiert und in eine Datei KUNDE2 eingestellt. In das erste Feld ZEILE der Datei KUNDE2 wird eine fortlaufende Nummer eingestellt, die mit eins beginnt und dann sortiert nach ORT, NACHNAME, VORNAME immer um eins erhöht ansteigt. Dieser Wert wird mit dem Subselect in der Klammer ermittelt, der jeweils zählt, wie viele Sätze in der Sortierfolge vorher kommen; hierbei wird der geschriebene Satz selber mitgezählt, um die Nummerierung mit eins beginnen zu lassen.
Dieses Verfahren ist allerdings nicht ohne Laufzeitaufwand, da der Subselect zur Ermittlung der Zeilennummer rekursiv ausgeführt werden muss. Zu beachten ist noch, dass die Sätze physikalisch nicht in der Reihenfolge der Zeilennummer eingestellt werden, dies ist jedoch ohne Bedeutung, da ja nach der Zeilennummer sortiert werden kann.
Den Autor Dieter Bender erreichenSie unter der e-Mail-Adresse: dieter.bender@midrangemagazin.de