Az explicit kurzorhoz hasonlóan a kurzorváltozó is egy aktív halmaz valamelyik sorának feldolgozására alkalmas. Amíg azonban a kurzor statikus, azaz egyetlen, a fordításkor már ismert SELECT utasításhoz kötődik, addig a kurzorváltozó dinamikus, amelyhez futási időben bármely típuskompatibilis kérdés hozzákapcsolható.
A kurzorváltozó használata az explicit kurzorok használatával megegyező módon történik (deklaráció, megnyitás, betöltés, lezárás).
A kurzorváltozó lényegében egy referencia típusú változó, amely mindig a hivatkozott sor címét tartalmazza.
A kurzorváltozó deklarálása két lépésben történik. Először létre kell hozni egy REF CURSOR saját típust, majd ezzel a típussal deklarálni egy változót. A típusdeklaráció szintaxisa:
TYPE név IS REF CURSOR [RETURN
{{ab_tábla_név | kurzor_név | kurzorváltozó_név}%ROWTYPE | rekord_név%TYPE|
rekordtípus_név |
kurzorreferenciatípus_név}];
Az ab_tábla_név olyan adatbázisbeli tábla vagy nézet neve, amelyik ismert a deklarációnál.
A kurzor_név egy korábban deklarált explicit kurzor, a kurzorváltozó_név egy kurzorváltozó neve.
A rekord_név egy korábban deklarált rekord neve.
A rekordtípus_név egy korábban deklarált RECORD típus neve.
A kurzorreferenciatípus_név egy már deklarált REF CURSOR típus.
Ha szerepel a RETURN utasításrész, akkor erős, egyébként gyenge kurzorreferencia típusról beszélhetünk. A kettő között a különbség abban áll, hogy az erős kurzorreferencia
típusnál a fordító tudja ellenőrizni a kapcsolt kérdés típuskompatibilitását, míg a gyengénél bármely kérdés kapcsolható.
A PL/SQL jelen verziója tartalmaz egy előre definiált gyenge kurzorreferencia típust,
ennek neve SYS_REFCURSOR. Saját gyenge kurzorreferencia típus deklarálása helyett javasoljuk ennek használatát.
Az általunk deklarált vagy előre definiált kurzorreferencia típussal aztán kurzorváltozót tudunk deklarálni.
1. példa
DECLARE
TYPE t_egyed IS RECORD ...;
/* gyenge típusú REF CURSOR */
TYPE t_refcursor IS REF CURSOR;
/* erős típusú REF CURSOR */
TYPE t_ref_egyed IS REF CURSOR RETURN t_egyed;
/* Kurzorváltozók */
v_Refcursor1 t_refcursor;
v_Refcursor2 SYS_REFCURSOR; -- ez is gyenge típusú
v_Egyedek1 t_ref_egyed;
v_Egyedek2 t_ref_egyed;
⋮
A kurzorváltozót is meg kell nyitni. A kurzorváltozóhoz a megnyitáskor kapcsolódik hozzá az aktív halmazt meghatározó lekérdezés. Az utasítás alakja:
OPEN kurzorváltozó_név FOR select_utasítás;
Egy adott kurzorváltozó bármennyi OPEN-FOR utasításban szerepelhet, egy újabb megnyitás előtt nem kell lezárni. Egy kurzorváltozó újramegnyitása azt jelenti, hogy egy új aktív halmaz jön létre, amelyre a kurzorváltozó hivatkozni fog és az előző törlődik.
A kurzorváltozó betöltése és lezárása a kurzoroknál tárgyalt FETCH és CLOSE utasításokkal történik.
Ha a kurzorváltozó egy alprogram formális paramétere és az alprogram csak betölti és lezárja azt, akkor a paraméterátadás IN vagy IN OUT lehet. Ha a megnyitása is az alprogramban történik, akkor a mód kötelezően IN OUT.
2. példa
DECLARE
TYPE t_egyed IS RECORD (id NUMBER, leiras VARCHAR2(100));
/* erős típusú REF CURSOR */
TYPE t_ref_egyed IS REF CURSOR RETURN t_egyed;
/* gyenge típusú REF CURSOR típust nem kell deklarálnunk,
a SYS_REFCURSOR típust használjuk helyette */
/* Kurzorváltozók */
v_Refcursor SYS_REFCURSOR;
v_Egyedek1 t_ref_egyed;
v_Egyedek2 t_ref_egyed;
v_Egyed t_egyed;
/* Megnyit egy gyengén típusos kurzort. */
PROCEDURE megnyit_konyv(p_cursor IN OUT SYS_REFCURSOR) IS
BEGIN
OPEN p_cursor FOR
SELECT id, cim FROM konyv;
END;
/* Megnyit egy erősen típusos kurzort. */
FUNCTION megnyit_ugyfel RETURN t_ref_egyed IS
rv t_ref_egyed;
BEGIN
OPEN rv FOR
SELECT id, nev FROM ugyfel;
RETURN rv;
END;
/* Egy sort betölt a kurzorból és visszaadja.
A visszatérési érték másolása miatt nem célszerű használni. */
FUNCTION betolt(p_cursor IN t_ref_egyed) RETURN t_egyed IS
rv t_egyed;
BEGIN
FETCH p_cursor INTO rv;
RETURN rv;
END;
/* Bezár egy tetszőleges kurzort. */
PROCEDURE bezar(p_cursor IN SYS_REFCURSOR) IS
BEGIN
IF p_cursor%ISOPEN THEN
CLOSE p_cursor;
END IF;
END;
BEGIN
/* Elemezze a típuskompatibilitási problémákat! */
megnyit_konyv(v_Egyedek1);
v_Refcursor := megnyit_ugyfel;
/* Innentől kezdve a v_Refcursor és a v_Egyedek2 ugyanazt
a kurzort jelenti! */
v_Egyedek2 := v_Refcursor;
v_Egyed := betolt(v_Egyedek2);
DBMS_OUTPUT.PUT_LINE(v_Egyed.id || ', ' || v_Egyed.leiras);
v_Egyed := betolt(v_Refcursor);
DBMS_OUTPUT.PUT_LINE(v_Egyed.id || ', ' || v_Egyed.leiras);
/* Most pedig v_Refcursor és a v_Egyedek1 egyezik meg. */
v_Refcursor := v_Egyedek1;
v_Egyed := betolt(v_Egyedek1);
DBMS_OUTPUT.PUT_LINE(v_Egyed.id || ', ' || v_Egyed.leiras);
v_Egyed := betolt(v_Refcursor);
DBMS_OUTPUT.PUT_LINE(v_Egyed.id || ', ' || v_Egyed.leiras);
bezar(v_Egyedek1);
bezar(v_Egyedek2);
BEGIN
/* Ezt a kurzort már bezártuk egyszer v_Egyedek1 néven! */
v_Egyed := betolt(v_Refcursor);
DBMS_OUTPUT.PUT_LINE(v_Egyed.id || ', ' || v_Egyed.leiras);
EXCEPTION
WHEN INVALID_CURSOR THEN
DBMS_OUTPUT.PUT_LINE('Tényleg be volt zárva!');
END;
/* Itt nem szerepelhetne sem v_Egyedek1, sem v_Egyedek2. */
OPEN v_Refcursor FOR
SELECT 'alma',2,3,4 FROM DUAL;
/* Ez az értékadás most kompatibilitási problémát eredményez!
Az előbb nem okozott hibát, mert az ellenőrzés futási időben történik. */
v_Egyedek2 := v_Refcursor;
EXCEPTION
WHEN OTHERS THEN
CLOSE v_Refcursor;
RAISE;
END;
/
/*
Eredmény:
5, Kovács János
10, Szabó Máté István
5, A római jog története és institúciói
10, A teljesség felé
Tényleg be volt zárva!
Hiba a(z) 1. sorban:
DECLARE
*
ORA-06504: PL/SQL: Az Eredményhalmaz-változók vagy a kérdés visszaadott típusai nem illeszkednek
ORA-06512: a(z) helyen a(z) 99. sornál
*/
A PL/SQL-ben kezelhetők ún. CURSOR kifejezések is. A CURSOR kifejezés egy beágyazott kérdés sorait kezeli kurzor segítségével. Ilyenkor a kurzor által kezelt aktív halmaz sorai értékeket és kurzorokat tartalmaznak. A feldolgozás során először mindig az aktív halmaz sorai töltődnek be, majd egy beágyazott ciklus segítségével a beágyazott kurzorok alapján a beágyazott kérdések sorai.
Egy CURSOR kifejezés szerepelhet kurzordeklaráció SELECT-jében vagy kurzorváltozóhoz kapcsolt SELECT-ben. Alakja:
CURSOR(select_utasítás)
Egy beágyazott kurzor implicit módon nyílik meg, amikor a szülő kurzor valamelyik sora betöltésre kerül. A beágyazott kurzor lezáródik, ha:
a szülő kurzor új sort tölt be;
a szülő kurzor lezáródik;
a szülő kurzorral történő betöltésnél hiba keletkezik.
3. példa (CURSOR kifejezés használatára)
DECLARE
TYPE t_Konyv_rec IS RECORD ( id konyv.id%TYPE, cim konyv.cim%TYPE);
TYPE t_konyvref IS REF CURSOR RETURN t_Konyv_rec;
/* Lekérdezzük az ügyfeleket és a kölcsönzött könyveiket,
azt az ügyfélt is, akinél nincs könyv.
A könyveket egy CURSOR kifejezés segítségével adjuk vissza. */
CURSOR cur_ugyfel_konyv IS
SELECT id, nev,
CURSOR(SELECT k.id, k.cim FROM konyv k, TABLE(konyvek) uk
WHERE k.id = uk.konyv_id) AS konyvlista
FROM ugyfel
ORDER BY UPPER(nev);
v_Uid ugyfel.id%TYPE;
v_Unev ugyfel.nev%TYPE;
v_Konyvek t_konyvref;
v_Konyv t_Konyv_rec;
BEGIN
OPEN cur_ugyfel_konyv;
LOOP
FETCH cur_ugyfel_konyv INTO v_Uid, v_Unev, v_Konyvek;
EXIT WHEN cur_ugyfel_konyv%NOTFOUND;
DBMS_OUTPUT.NEW_LINE;
DBMS_OUTPUT.PUT_LINE('Ügyfél: ' || v_Uid || ', ' || v_Unev);
/* Most a beágyazott kurzor elemeit írjuk ki, ha nem üres.
A beágyazott kurzort nem kell külön megnyitni és lezárni sem. */
FETCH v_Konyvek INTO v_Konyv;
IF v_Konyvek%FOUND THEN
DBMS_OUTPUT.PUT_LINE(' A kölcsönzött könyvek:');
WHILE v_Konyvek%FOUND LOOP
DBMS_OUTPUT.PUT_LINE(' ' || v_Konyv.id || ', ' || v_Konyv.cim);
FETCH v_Konyvek INTO v_Konyv;
END LOOP;
ELSE
DBMS_OUTPUT.PUT_LINE(' jelenleg nem kölcsönöz könyvet.');
END IF;
END LOOP;
CLOSE cur_ugyfel_konyv;
END;
/
/* Eredmény:
Ügyfél: 25, Erdei Anita
A kölcsönzött könyvek:
35, A critical introduction to twentieth-century American drama - Volume 2
Ügyfél: 35, Jaripekka Hämälainen
A kölcsönzött könyvek:
35, A critical introduction to twentieth-century American drama - Volume 2
40, The Norton Anthology of American Literature - Second Edition - Volume 2
Ügyfél: 15, József István
A kölcsönzött könyvek:
15, Piszkos Fred és a többiek
20, ECOOP 2001 - Object-Oriented Programming
25, Java - start!
45, Matematikai zseblexikon
50, Matematikai Kézikönyv
Ügyfél: 30, Komor Ágnes
A kölcsönzött könyvek:
5, A római jog története és institúciói
10, A teljesség felé
Ügyfél: 5, Kovács János
jelenleg nem kölcsönöz könyvet.
Ügyfél: 10, Szabó Máté István
A kölcsönzött könyvek:
30, SQL:1999 Understanding Relational Language Components
45, Matematikai zseblexikon
50, Matematikai Kézikönyv
Ügyfél: 20, Tóth László
A kölcsönzött könyvek:
30, SQL:1999 Understanding Relational Language Components
A PL/SQL eljárás sikeresen befejeződött.
*/
Az Oracle10g a kurzorváltozók használatára az alábbi korlátozásokat alkalmazza:
Kurzorváltozó nem deklarálható csomagban.
Távoli alprogramoknak nem adható át kurzorváltozó értéke.
Kurzorreferencia típusú gazdaváltozó PL/SQL-nek való átadása esetén a szerveroldali betöltés csak akkor lehetséges, ha a megnyitás is ugyanazon szerverhívásban történt.
A kurzorváltozónak nem adható NULL érték.
Kurzorváltozók értéke nem hasonlítható össze.
Adatbázistábla oszlopában nem tárolható kurzorreferencia típusú érték.
Kollekció eleme nem lehet kurzorreferencia típusú érték.