pl/sql: Frage zu Cursor

zuckerbrini

Mitglied
Hi!
Ich hoffe das ich hier im richtigen Forum bin.
Steh gerade ziemlich auf der Leitung.....

Ich habe eine Prozedure die einen Cursor hat. In einer Cursor-For-Schleife werden nun verschiedenste Dinge gemacht. Nun möchte ich die ganze Schleife nochmal ausführen nur mit einem anderen Cursor.
Wie funktioniert das?
Hab mir überlegt eine Prozedure in der Prodzedure zu schreiben, aber da kann ich keinen Cursor zurückgeben (weiß zumindest nicht wie).
Oder die andere Überlegen eine Variable zu machen die, die dann je nachdem auf den einen oder den anderen Cursor gesetzt ist. Hab ich aber auch keine Ahnung wie ich das realisieren soll.

Kann mir da irgendjemand weiterhelfen?

Wichtig ist noch das eben alles in einer Procedure sein soll.

Danke schon mal im Voraus
 
Es wäre hilfreich, wenn du deinen Code posten würdest. Dann kann man sich ein bisschen besser vorstellen was du machst.

Prinzipiell ist es möglich Cursor zwischen Prozeduren hin-, und herzuschicken, auch inline. Wichtig ist, dass wenn du zwei unterschiedliche Cursor in ein und derselben Schleife durchloopen willst, dass sie vom selben Typ sind. D.h. sie sollten keine generischen Cursor vom Typ SYS_REFCURSOR sein, sondern besser z.B. CURSOR OF table%ROWTYPE.

Poste mal dein Codefragment, dann sehen wir weiter...
 
Code:
CREATE OR REPLACE PROCEDURE bla IS
  CURSOR eins IS SELECT bla, bu from person p, datei d where p.id=d.id;
  CURSOR zwei IS SELECT bla, bu from person p, datei d where p.id NOT d.id;
BEGIN
FOR eins IN cursor LOOP
 --tu was
END LOOP;
END;

jetzt will ich eben nach dem de for-schleife mit dem cursor eins durchgelaufen ist, das ganze auch nicht mit dem Cursor zwei machen.
Hoff das ist so jetzt verständlicher.
 
Zuletzt bearbeitet:
Hier mal ein generisches Beispiel:

SQL:
declare

	TYPE cur_t IS REF CURSOR RETURN T%ROWTYPE;
	
	mycur cur_t;
	
	procedure loop_t( cur IN cur_t )
	is
		TYPE table_t IS TABLE OF T%ROWTYPE;
		my_data	table_t;
	begin
		FETCH cur BULK COLLECT INTO my_data;
		CLOSE cur;
		
		DBMS_OUTPUT.PUT_LINE( 'FETCHING CURSOR: ' );
		
		IF my_data.COUNT > 0 THEN
			FOR i IN my_data.FIRST .. my_data.LAST LOOP
				DBMS_OUTPUT.PUT_LINE( my_data(i).ID );
			END LOOP;
		END IF;
		
		DBMS_OUTPUT.PUT_LINE( 'DONE ' );
	end;

begin
	
	OPEN mycur FOR
		SELECT * FROM T WHERE ID < 40;
		
	loop_t( mycur );
	
	OPEN mycur FOR
		SELECT * FROM T WHERE ID > 40;
	
	loop_t( mycur );

end;
/

Die Ausgabe:
Code:
FETCHING CURSOR:
20
26
27
28
29
30
31
32
33
34
35
36
37
38
39
DONE
FETCHING CURSOR:
41
42
43
44
45
46
47
48
49
50
DONE

Nun zur Erklärung:

Ich habe einen anonymen PL/SQL Block indem eine Prozedur definiert ist. Man kann natürlich auch eine Prozedur nehmen anstatt eines Blocks. Die interne Prozedur heißt loop_t und erwartet als Input Parameter einen Cursor vom Typ cur_t. Dieser Typ ist im Block definiert und liefert Zeilen aus meiner Tabelle T zurück.
In der Prozedur werden die Daten des bereits geöffnete Cursors! in eine PL/SQL Table geladen und durchgeloopt. Mit dem BULK COLLECT gehts einfach schneller als mit einer normalen FOR LOOP und einzel-fetches.
Aufgerufen wird die interne Prozedur 2 mal aus meinem anonymen PL/SQL Block heraus. Dort wird zunächt der Cursor geöffnet, und dann der loop_t Prozedur übergeben.
 
Zuletzt bearbeitet von einem Moderator:
Danke erstmal. Soweit sieht der Code ganz gut aus. Danke auch das ich ihnn verstehe nur ist ma das mit dem T%ROWTYPE unklar. Was %ROWTYPE macht ist mir klar, aber was ist dieses T. Muss ich mir da einen "Beispiel-Cusor" anlegen?
So in etwa
CURSOR T IS Select * from person, tier, datei;
 
In meinem Beispiel ist es recht einfach gehalten, da T bei mir einfach nur eine Tabelle ist. Wenn du natürlich ein komplexeres SELECT aus mehreren Tabellen hast funktioniert T%ROWTYPE nicht.

Im Grunde kannst du auch durchgehend mit Impliziten Cursorn arbeiten, d.h.
Code:
TYPE cur_t IS REF CURSOR;
genügt, auch ohne Angabe eines RETURN Wertes. Wo man nicht implizit arbeiten kann ist mit der TABLE OF innerhalb der Prozedur. Hier muss Oracle schon zur Compilezeit wissen wieviel Spalten und welchen Typ die Tabelle haben soll.
In diesem Fall kannst du aber tatsächlich mit einem exemplarischen Cursor arbeiten, z.B:
SQL:
	cursor t2 is 
		select * from a, b, c where a.id = b.id --usw 
		;

	TYPE cur_t IS REF CURSOR RETURN T2%ROWTYPE;

--snip

	procedure loop_t( cur IN cur_t )
	is
		TYPE table_t IS TABLE OF t2%ROWTYPE;
		my_data	table_t;

--snip
 
Zuletzt bearbeitet von einem Moderator:
Hi!

Leider bekomme ich das Ganze noch immer nicht hin.

Hab mir jetzt mal einen exemplarischen Cursor angelegt:
Code:
CURSOR T IS SELECT p.name, dp.adr, t.name, dt.adr FROM person p, daten dp, 
  tier t, daten dt;

TYPE cur_t IS REF CURSOR RETURN T%ROWTYPE;
mycur cur_t;

OPEN mycur FOR 
   SELECT p.name, dp.adr, t.name, dt.adr FROM person p, daten dp, 
  tier t, daten dt  WHERE p.id=dp.id and t.id=dt.id;
loop_t(mycur);

loop_t(mycur) hab ich unverändert gelassen. Hab jetzt erstmal nur einen Aufruf von loop_t um das ganze zu testen.
Leider bekomme ich aber immer expression 'MY_DATA' in the INTO list is of wrong type
Das ganze kommt genau bei der FETCH... Anweisung. Hab schon alles mögliche ausprobiert und jede Menge im Internet gelesen, aber ich komm einfach nicht drauf....
 
Habe jetzt mal 2 Tabellen von dir angelegt: Person und Daten. Hier mein Code der zumindest auf einer Oracle 10gR2 EE funktioniert:

SQL:
declare
 
	CURSOR T IS 
		SELECT 	p.name, d.id
		FROM 	person p, daten d;


    TYPE cur_t IS REF CURSOR RETURN T%ROWTYPE;
   
    mycur cur_t;
   
    procedure loop_t( cur IN cur_t )
    IS
        TYPE table_t IS TABLE OF T%ROWTYPE;
        my_data table_t;
    begin
        FETCH cur BULK COLLECT INTO my_data;
        CLOSE cur;
       
        DBMS_OUTPUT.PUT_LINE( 'FETCHING CURSOR: ' );
       
        IF my_data.COUNT > 0 THEN
            FOR i IN my_data.FIRST .. my_data.LAST LOOP
                DBMS_OUTPUT.PUT_LINE( my_data(i).ID || ' ' || my_data(i).Name );
            END LOOP;
        END IF;
       
        DBMS_OUTPUT.PUT_LINE( 'DONE ' );
    end;
 
begin
   
    OPEN mycur FOR
		SELECT 	p.name, d.id
		FROM 	person p, daten d
		WHERE p.name='Andreas' and d.id > 40;
       
    loop_t( mycur );
   
    OPEN mycur FOR
		SELECT 	p.name, d.id
		FROM 	person p, daten d
		WHERE p.name='Andreas' and d.id < 40;
   
    loop_t( mycur );
 
end;
/
 
Zuletzt bearbeitet von einem Moderator:
Hab soeben gelesen das es BULK COLLECTION erst ab Oracle 10 gibt. Habe Leider vergessen zu erwähnen das ich Orcale 9 habe.
Hab deshalbe die Zeile
Code:
FETCH cur BULK COLLECT INTO my_data;
gegen
Code:
FETCH cur INTO my_date;
ausgetauscht.
Bekomme nachwievor noch die gleiche Fehlermeldung.
Muss ich sonst noch irgendwas ändern bzw. beachten das es erst ab Oracle10 gibt?
Hoff ich nerv nicht schon.....
 
Zurück