Oracle: Resultset aus StoredProcedure zurückgeben

Hallo da draussen!

Wir migrieren unser Produkt zurzeit von MS-SQL auf ORACLE. Auf MS-SQL hatten wir eine Stored Procedure die etwa so ausschaut:

BEGIN
/*DO SOME STUFF*/
SELECT * FROM TEMPTABLE
END

Nach dem Ausführen der Prozedur hatten wir direkt das Resultset aus der Abfrage zur Verfügung (gleich wie das ausführen eines einfachen SELECT-Statement). Oracle lässt nun aber (scheinbar) ein SELECT in dieser Form in einer Stored Procedure gar nicht zu. Statt dessen erwartet es ein „SELECT INTO“.
Wir sind darauf angewiesen, dass
(A) Die Anweisungen und die anschliessende Abfrage in einer Procedure oder Function ablaufen und
(B) sich das Ausführen dieser Procedure gleich verhält, wie das direkte ausführen eines SELEC.
Kann mir da einer einen Tip geben wie das geht?

Vielen Dank schon mal im voraus.
Gruss: Christian

Hallo,

Prozeduren haben halt keinen Rückgabewert. Du kannst entweder das Ergebnis in die temporäre Tabelle schreiben und danach das Select auf diese Tabelle machen oder Du benutzt eine Funktion mit einem Tabellenwertigen Returnwert.

Gruß

Peter

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Oracle: REF CURSOR, out-param explizit deklarieren
Hallo Christian,

das Zauberwort bei Oracle heisst REF CURSOR. Während bei T-SQL mit einem SELECT in einer procedure implizit eine Cursor-Variable angelegt wird, muss dies bei Oracle explizit passieren, hier ein Beispiel (nur um den Sachverhalt zu verdeutlichen, kann sein, dass der Code nicht kompiliert…): Die Prozedur bekommt eine id übergeben und hat zwei out-Parameter: Einmal einen int-Wert, den ich der Einfachheit halber mit einer Konstante belege und dann einen Tupel-Cursor (als Ergebnis eines SELECTs).

So sieht der Code mit T-SQL / MS-SQL-Server aus:

create procedure sqlserver\_style (@id int, @value int output)
as
begin
 set @value = 4711
 select \* from demo\_table where id = @id
end
go

Jetzt mit PL/SQL (Oracle)

create or replace package oracle\_style
as
 type gencurtype is ref cursor;
 procedure oracle\_style (v\_id in number, v\_value out number, v\_refcursor out oracle\_style.gencurtype);
end;
/

create or replace package body oracle\_style
as
 procedure oracle\_style (v\_id in number, v\_value out number, v\_refcursor out oracle\_style.gencurtype)
 is
 begin
 v\_value := 4711;
 open v\_refcursor for select \* from demo\_table where id = v\_id;
 end;
end;
/

Wie schon gesagt, soll Dir nur als Hint dienen, wie Du das Thema angehen kannst, erspart möglicherweise nicht Doku-Studium :wink:

Noch ein paar Hinweise zur Portierung von T-SQL nach PL/SQL:

  1. bei T-SQL werden Parameter über @ referenziert und können somit z.B. von Attributen unterschieden werden. Dies gibt’s bei Oracle nicht, deshalb ist eine Namenskonvention (wie bei meinem Beispiel mit dem Präfix v_ für variable) hilfreich.

  2. Du wirst die Prozedur wohl über JDBC, … oder was auch immer aufrufen. Die genaue Syntax und die Parameterbindung hängt typischerweise von Deinem Dataprovider ab, für manche ist die Reihenfolge wesentlich, andere führen die Bindung über den Parameternamen durch… in der Doku nachschauen, wie sich der Dataprovider bzgl. SQL-Server / Oracle verhält.

  3. Du bekommt einen geöffneten Tupel-Cursor zurück, dieser muss vom aufrufenden Code (explizit oder implizit) geschlossen werden, sonst verbrätst Du Ressourcen - typischer Effekt: in der Testumgebung funktioniert alles, in der Produktion fällst Du nach 5 Min. über den Oracle-Fehler „too many open cursors“

  4. Noch ein interessanter Hinweis: Bei SQL-Server müssen zuerst ALLE zurückgegebenen Tupel-Cursor VOLLSTÄNDIG abgearbeitet werden (via fetch), erst dann stehen die restlichen Parameter zur Verfügung, hier im Beispiel out-Param value. Oracle hat diese Restriktion nicht, der out-Param v_value steht sofort zur Verfügung, egal ob und wieviele Tupel von v_refcursor abgeholt wurden.

Hoffe, dass Dir diese Infos weiterhelfen, Grüße,
Bernhard

Vielen Dank
Hallo Peter und Berhard

Erst mal vielen Dank für Eure Antworten.

Ich werd diese Ansätze mal prüfen und sehen ob ich so weiter komme. Ansonsten muss ich mir wohl einen alternativen Lösungsweg suchen.

Vielleicht zum besseren Verständnis wo genau das Problem auftaucht:

Wir rufen von unserer Software die „Crystal Report – Printengine“ (CRPE) über dll-Calls auf. Wir übergeben der CRPE den Namen der Procedure und die Parameterwerte. Anschliessend übernimmt die CRPE die Kontrolle, d.h. wir haben keine Kontrolle mehr über Rückgabewerte, seinen das Tabellen oder Cursor-Referenzen.

Anyway. Mal schauen was ich machen kann. Gruss: Christian

Happy End: Oracle: Resultset aus StoredProcedure z
Für alle, dies interessiert! Oracle Stored Procedures werden von Crystal Reports folgendermassen angesprochen:

1. Man benötigt eine Package, die einen REF CURSOR definiert. Z.B:
CREATE OR REPLACE PACKAGE schema.packageName AS
Type cursorName IS REF CURSOR;
END packageName;

2. Man benötigt eine Stored Procedure mit einem Parameter vom oben genannten Typ. Z.B:
CREATE OR REPLACE PROCEDURE schema.sp_procName(
pRefCursor IN OUT packageName. cursorName) /*Wichtig: Parameter muss IN/OUT sein!*/
AS
strSQL VARCHAR2(255);
BEGIN
strSQL:= 'SELECT * FROM tblXY’;
OPEN pRefCursor FOR strSQL;
END;

3. Wenn man mit ODBC arbeitet, dann muss es ein CrystalReports-Oracle-ODBC-Treiber sein. Dort muss die Option „Procedure Return Results“ ausgewählt sein. Erwähnenswert ist ebenfalls, dass es für jede Version von Crystal Reports jeweils einen eigenen Treiber für jede Version von Oracle gibt. Alternativ kann man auch einen „Native Oracle Driver“ verwenden, darüber kann ich aber keine Aussagen machen.

Vielen Dank an alle, die mich bei der Suche nach einer Lösung unterstützt haben. Gruss: Christian