PDA

View Full Version : RPG-CALL mit Parameter aus VB...



Seiten : 1 [2] 3 4

Fuerchau
05-05-04, 14:22
Da hast du ja was gemacht, was du wohl selber nicht verstehst !

Also:

as400db.Execute "CREATE PROCEDURE MyLib.MyPgm (IN :PARM1 CHAR (512), IN :PARM2 DEC(15, 5)) LANGUAGE RPG NOT DETERMINISTIC NO SQL EXTERNAL NAME MyLib.MyPgm PARAMETER STYLE GENERAL", , adExecuteNoRecords

Du solltest dein Programm und die Lib benennen, denn das wird der Name der Prozedur !
Die 2. Angabe <LIB>.<MYPGM> verweist dann tatsächlich auf das Programm !

Die Parameter definierst du auch entsprechend Deinem Programm mit IN/OUT/INOUT, Typ und Länge !

DANN kannst du auch das Programm aufrufen.

PS:
Mein Beispiel war für den Aufruf des Programmes QCMDEXC um Kommandos per SQL durchzuführen.

BeWe
06-05-04, 08:00
Richtig,

zugegeben, erst hab ich es wirklich nicht verstanden, wie man
ja an dem obrigen Beispiel erkennen kann. Über Nacht kam dann
die Erleuchtung und heute Morgen konnte ich es korrigieren...

as400db.Execute "CREATE PROCEDURE QTEMP.QCMDPSEU10 (OUT :PARM1 CHAR (1)) LANGUAGE RPG NOT DETERMINISTIC NO SQL EXTERNAL NAME SAGPLIB.PSEULABRPG PARAMETER STYLE GENERAL", , adExecuteNoRecords

...ist der richtige Ausdruck.

Allerdings kann man es dann immer noch nicht auslesen!
Man muss dann, vor dem Ausführen des Command.Execute, noch folgendes mit dem Command-Objekt machen.

as400cmd.Parameters(0).Direction = adParamOutput
as400cmd.Parameters(0).Type = adChar
as400cmd.Parameters(0).Size = 1

Dann ist es auch nicht mehr nötig, das Array mit den Parametern zu übergeben,
was so leider nämlich nicht funktioniert hat.

Das ADODB.Command-Objekt wusste komischer weise, dass es einen Parameter hat
nach dieser Ausführung auf das Connection-Objekt :confused: Woher? Der Versuch, einen
zweiten Parameter (Index 1) zu initialisieren führte zu einer Fehlermeldung?

Jedenfalls vielen, vielen Dank für die Geduldige Unterstützung und viele Grüße
aus dem sonnigen Sauerland, BeWe

Fuerchau
06-05-04, 09:06
Jedes "?" bedeutet einen Parameter!

Bei IN-Parametern kann der SQL-Treiber die korrekte Umsetzung selber durchführen.
Bei OUT/INOUT-Parametern ist eine Beschreibung der Parameter erforderlich, da keine automatische Anpssung möglich ist.

Daher die Beschreibung der Parameter:
as400cmd.Parameters.Append as400cmd.CreateParameter("PARM1", adChar, adParamOutput, 1)

Beim Zugriff auf die Parameter mit Index wird der CommandText an den Treiber (und damit an die AS/400) zur Ermittlung der Anzahl Parameter gesendet.
Wenn dann die Syntax e.g. nicht stimmt, kommt es zu einer Ausnahme.

"?" gelten als sog. Parametermarker und sind bei der wiederholten Ausführung erheblich schneller. Viele Programme arbeiten immer noch mit voll dynamischem SQL (geht ja so schön einfach mit VB), was die AS/400 aber zwingt den SQL immer neu zu analysieren, Zugriffswege zu ermitteln usw.
Definiere ich einen SQL aber mit "?", erfolgt die Analyse nur 1 Mal und die Ausführung (Execute) erfolgt sofort.
Ausserdem ist die Programmierung damit erheblich einfacher, als jedesmal die SQL zusammenzusetzen.
Problematisch ist das nähmlich bei Zeichenketten, die selber ein Hochkomma enthalten. Bei Parametermarkern braucht man sich nicht zu kümmern, beim zusammengsetzten SQL müssen diese aber verdoppelt werden.
Beim internationalen Einsatz ist auch noch Decimalpoint/Decimalcomma entscheidend, was bei "?" keine Rolle spielt.

Wie übergebe ich nun Parameter ?
IN/INOUT-Parameter können entweder vor dem Execute mittels Zugriff erfolgen:
as400cmd!Parm1 = Wert ' wenn der Parameter einen Namen hat
as400cmd(0) = Wert ' über Index
oder im Execute selber als Array im 2.Parameter (daher mein Beispiel).

OUT-Parameter können halt nicht übergeben werden, aber trotzdem kann dies über Execute erfolgen, wenn man eine Mischung mit IN/OUT hat:
as400cmd.Execute(, Array(Inparm1, Inparm2, ,InoutParm4),)

Anmerkung:
Für einen korrekten Namen der Prozedur solltest du nicht QTEMP wählen, da Prozeduren (im Gegensatz zu anderen QTEMP-Objekten) NICHT wieder entfernt werden. Ich habe nur QTEMP gewählt, weil ich ja QCMDEXC aufrufen wollte.

Wenn die Prozedur vorhanden ist, aber geändert werden muss, kannst du per "drop procedure Lib.Name" die Prozedur wieder entfernen (auch von STRSQL auf der AS/400).

Welche Prozeduren vorhanden sind, lässt sich über "select * from procedures" erfragen.

BeWe
06-05-04, 11:32
Nicht schlecht, nicht schlecht :cool:

genau das wäre meine nächste nervige Frage gewesen ;-).
Allerdings habe ich bei dem ganzen Probieren mehrere
Procedures mit unterschiedlichen Paramterzusammenstellungen
erstellt eine habe ich mit

drop procedure QTEMP/Procname (CHAR)

gelöscht bekommen, aber zwei weitere nicht mehr. Gibt es
dabei so was wie Wildcards?

Delete * from procedures where Procname = "ProcName"
geht leider nicht :)

Vielen Dank nochmal für die Tips, habe jetzt schon ein paar
Programme erstellen können bei denen 29 Parameter über-
geben werden und es ist doch noch recht schnell.

Fuerchau
06-05-04, 12:24
Welche Proceduren da sind, kannst du ja aus "procedures" erfahren.
Welche Parameter eine Prozedur hat erfährst du aus "Sysparms" !

Proceduren müssen leider genauso wies sie erstellt wurden, gelöscht werden.
Es können ja durchaus gleiche Prozeduren mit unterschiedlichen Parametern erstellt werden. Dies nennt man "Overloading", bekannt aus der C++-Programmierung.

Beim "drop procedure" muss die genaue Parameterliste angegeben werden, im Zweifel sogar die Ausprägung:

drop procedure lib.name (char, char, ...)
drop procedure lib.name (char(10), char(5), ...)

usw.

Das gleiche gibt es übrigens auch für Funktionen "create function".
Vorteil hierbei ist, dass diese Funktionen im "Select" bzw. überall wo Scalar-Funktionen erlaubt sind, verwendet werden können.
Seit V5 kann man sogar reine SQL-Funktionen erstellen, da der intern verwendete C-Kompiler nicht extra lizensiert werden muss.
Ich kann also relativ komfortable Funktionen in SQL erstellen ohne RPG bemühen zu müssen.

BeWe
06-05-04, 13:13
Ja so klappt das anscheinend recht gut :), und ich kann
nur abermals tausend Dank aussagen.

In C++ ist das Überladen von Funktionen, mit genügend
Vorsicht ja durchaus eine Interessante Sache. Wusste
bis eben nicht, dass dies auch hier als solches Phänomen
zu verstehen ist.

Aber ganz ehrlich,... habe mal gehört, dass ein gut durch-
dachtest RPG-Programm schneller sei, als SQL. Ist da etwas
dran, oder ist das falsch?

Fuerchau
06-05-04, 14:45
Ja und Nein !
Es gibt 1000+ Argumente, die da eine Rolle spielen.

1. DB-Design
Es gibt Designs, da ist RPG einfach schneller, da SQL damit nicht zurechtkommt (Stichwort Normalisierung). Probleme bieten z.B. Schlüsselungleichheiten (Numerisch/Alpha) bei Join's oder Beziehungen, die nur per Programm aufgelöst werden können.

2. Recordlevel-Access (Einzelsatzverarbeitung)
Hier sollte man bei RPG folgendes wissen:
Dateizugriffe erfolgen immer über interne Puffer, also nie mit den Feldern direkt.
Durch die F-Bestimmung wird ja automatisch eine I-Bestimmung generiert. Der Compiler generiert aber ausschließlich die Felder, die eine Referenz aufweisen.
Nach einem READ/CHAIN werden aus dem Puffer die Felder gefüllt und vor dem WRITE/UPDATE der Puffer aus den Feldern gefüllt.
Arbeitet man aber mit einer externen DS, die alle Felder enthält auch wenn man sie nicht benötigt, werden jede Menge Move's generiert, die nichts als Zeit kosten.

Bei SQL sollte man IMMER die Felder selektieren (und fetchen) die man benötigt, man spart sich da also ein bißschen. Merkbar ist das aber nur bei sehr vielen (>100.000) Datensätzen.

Bei der Feldart sollte man darauf achten, dass nach Möglichkeit der gleiche Typ verwendet wird, das spart Konvertierungen.
Leider kennen DSPF/PRTF's keine gepackten Felder (auch wenn man sie definiert, der Compiler macht immer zoned daraus), was fast immer zu einer Konvertierung führt.

Auch SQL führt Anpassungen durch, die man aber durch explizite Definition minimieren kann.

3. Massen-Funktionen
Ganz klar bietet SQL den Vorteil bei Select mit Gruppierungen, Scalar-Funktionen o.ä.
Hier reicht oftmals ein Select, was in RPG mühsam programmiert und berechnet werden muss. Hier ist SQL schneller, da dies auf sehr tiefer Ebene in der DB gelöst wird.
Auch ein Massen-Update sowie Massen-Delete bietet große Vorteile.

Fazit:
Es kommt immer auf die gewünschte Funktion an, ob SQL oder RPG schneller ist.
Übrigens: COBOL kennt viele Overheads von RPG nicht und ist insbesonders bei nativen Dateizugriffen schneller (hier wird im Programm direkt mit den Dateipuffern gearbeitet).

Ein großer Vorteil von SQL ist, dass ich Optimierungen der DB vornehmen kann, ohne an den Programmen etwas zu ändern (Formatebenen-Id) oder einfach nur Spalten hinzufügen kann.
Ist ein SQL-Zugriff langsam, kann ich ihn beschleunigen indem ich einen Index erstelle. Will ich das RPG-Programm beschleunigen erfordert das ggf. ein Redesign.

Fuerchau
07-05-04, 08:32
Nachtrag

29 Parameter sind ja doch releativ viel, da die Gesamtanzahl Paramter auf (ich glaube) 32 beschränkt ist.
Wenn dies alles Ausgabeparameter sind, bietet sich eine andere, flexiblere Lösung an:

as400db.Execute "CREATE PROCEDURE MyLib.MyPgm (IN :PARM1 CHAR (512), IN :PARM2 DEC(15, 5)) LANGUAGE RPG RESULT SET 1 NOT DETERMINISTIC NO SQL EXTERNAL NAME MyLib.MyPgm PARAMETER STYLE GENERAL", , adExecuteNoRecords

Damit kannst du eine dynamisches Recordset zurückgeben:

h dftactgrp(*no) actgrp('MyGroup')

d MyStruct ds
d Field1 10
d Field2 5
d Field3 10p 2
:

/exec sql set result sets array MyStruct for 1 row
/end-exec

eval *inlr = *off

Wichtig ist, das das Programm aktiv bleiben muss, da auf die Variablen von SQL noch zugegriffen werden muss.

Vorteil:
Die Anzahl der Prozedur-Paramter reduziert sich und bei Erweiterung der Return-Werte braucht die Prozedur nicht neu beschrieben werden.

Die Anzahl der Zeilen kann auch dynamisch festgelegt werden:

d MyCount s 5p 0
d MyStruct ds dim(1000)

/exec sql set result sets array MyStruct for :MyCount row
/end-exec

Anzahl kann natürlich auch 0 sein (leeres Recordset).

Mit "set MyRcd = as400cmd.Execute" wird dann ein Recordset zurückgegeben.

Ich denke, dass dieses Verfahren für Dich vielleicht die bessere Lösung darstellt.

BeWe
11-05-04, 11:35
Hallo Fuerchau,

vielen Dank für den Tip, werde das auf jeden Fall mal versuchen
zu testen, jedoch weiss ich nicht, wie ich in RPG eine Struktur
zurückgeben kann, bzw. wie dann der Parameter definiert werden
muss.

Folgendes Problem hat sich neuerdings ergeben...

D UrProg s 11S 2 DIM(12)

anstatt 12 einzelne Paramter gilt es nun dieses (ich denke mal)
eindimensionale Array zurückzugeben, wobei ich mich frage
welchen Typ für diesen Parameter ich dann bei der CREATE PROCEDURE
angeben soll. Char und Dec doch sicher nicht, oder?

Weisst du da eine Lösung bezüglich dieses Problems?

Nochmals danke für den Tip, und sorry für die späte Antwort.
PS.: lautet der SQL-Code in deinem Beispiel wirklich so, oder ist
das einfach nur englischer Pseudo-Code :confused: :)

BeWe
11-05-04, 12:15
Nachtrag:

** umwandeln mit COMMIT *NONE
** ALWCPYDTA *NO
***********************************************
h dftactgrp(*no) actgrp('MyGroup')
/free
d MyStruct ds
d Field1 10
d Field2 5
d Field3 10S 2
:

/exec sql
set result sets array MyStruct for 1 row
/end-exec

*inlr = *off

/end-free


Habe das Programm so erstellen wollen, wobei ich allerdings
auf mehrere Fehler gestoßen bin. Zum Beispiel einen 40er
Fehler der da heist "Das Umwandlungsprogramm kann nicht
bestimmen wie das Programm enden kann"

Was mache ich falsch? Semikolons vergessen? kommen die
ausnahmslos hinter jede Zeile?