12. fejezet - Kollekciók

Egy kollekció azonos típusú adatelemek egy rendezett együttese. A kollekción belül minden elemnek egy egyedi indexe van, amely egyértelműen meghatározza az elem kollekción belüli helyét. A PL/SQL három kollekciótípust kezel, ezek az asszociatív tömb, a beágyazott tábla és a dinamikus (vagy változó méretű) tömb.

A kollekciók a harmadik generációs programozási nyelvek tömb fogalmával analóg eszközök a PL/SQL-ben. A kollekciók mindig egydimenziósak és indexeik egész típusúak, kivéve az asszociatív tömböt, amely indexelhető sztringgel is. A többdimenziós tömböket olyan kollekciókkal tudjuk kezelni, melyek elemei maguk is kollekciók.

A beágyazott táblák és a dinamikus tömbök elemei lehetnek objektumtípus példányai és ők lehetnek objektumtípus attribútumai. A kollekciók átadhatók paraméterként, így segítségükkel adatbázis tábláinak oszlopai mozgathatók az alkalmazások és az adatbázis között.

Az asszociatív tömb lényegében egy hash tábla, indexei (amelyek itt a kulcs szerepét játsszák) tetszőleges egészek vagy sztringek lehetnek. A beágyazott tábla és dinamikus tömb indexeinek alsó határa 1. Az asszociatív tömb esetén az indexeknek sem az alsó, sem a felső határa, beágyazott táblánál a felső határ nem rögzített.

Mindhárom kollekciótípus deklarálható PL/SQL blokk, alprogram és csomag deklarációs részében, a beágyazott tábla és dinamikus tömb típusok létrehozhatók adatbázis-objektumokként is a CREATE TYPE utasítással.

Az asszociatív tömb csak a PL/SQL programokban használható, a másik két kollekciótípus viszont lehet adatbázistábla oszlopának típusa is. Ilyen beágyazott tábla az ugyfel tábla konyvek oszlopa, és ilyen dinamikus tömb a konyv tábla szerzo oszlopa.

Egy dinamikus tömb típusának deklarációjánál meg kell adni egy maximális méretet, ami rögzíti az indexek felső határát. Az adott típusú dinamikus tömb ezután változó számú elemet tartalmazhat, a nulla darabtól a megadott maximális értékig. Egy dinamikus tömbben az elemek mindig folytonosan, a kisebb indexű „helyeken” helyezkednek el. Egy dinamikus tömb bővíthető (a maximális méretig) új elemmel a végén, de az egyszer bevitt lemek nem törölhetők, csak cserélhetők. Dinamikus tömb típusú oszlop értékei 4K méret alatt a táblában, efölött ugyanazon táblaterület egy külön helyén, egy tárolótáblában tárolódnak.

A beágyazott táblánál az elemek szétszórtan helyezkednek el, az elemek között lehetnek „lyukak”. Új elemeket tetszőleges indexű helyre bevihetünk és bármelyik elem törölhető (a helyén egy „lyuk” keletkezik).

A beágyazott tábla típusú oszlop értékei soha nem a táblában, hanem mindig a tárolótáblában helyezkednek el. A tárolótábla saját táblaterülettel és egyéb fizikai paraméterekkel rendelkezhet.

Explicit módon nem inicializált kollekciók esetén a beágyazott tábla és a dinamikus tömb automatikusan NULL kezdőértéket kap (tehát maga a kollekció, és nem az elemei), az asszociatív tömb viszont nem (csak egyszerűen nincs egyetlen eleme sem).

Az asszociatív tömb és a gazdanyelvek tömbje között a PL/SQL automatikus konverziót biztosít.

A kollekciótípusokat is felhasználói típusként kell deklarálni. Tekintsük át az egyes kollekciótípusok

deklarációjának formáját.

Asszociatív tömb:

TYPE név IS TABLE OF elemtípus [NOT NULL]

INDEX BY indextípus;

Az indextípus egy PLS_INTEGER, BINARY_INTEGER vagy VARCHAR2 típus vagy altípus specifikáció.

Beágyazott tábla:

TYPE név IS TABLE OF elemtípus [NOT NULL];

Dinamikus tömb:

TYPE név IS {VARRAY | VARYING ARRAY} (max_méret)

OF elemtípus [NOT NULL];

A név a kollekciótípus neve, amelyet ezen deklaráció után tetszőleges kollekció deklarációjában használhatunk típusként az alábbi módon:

kollekciónév kollekciótípus_név;

A max_méret pozitív egész literál, a dinamikus tömb indexeinek felső határát adja meg. Az

elemtípus egy PL/SQL adattípus, amely nem lehet REF CURSOR.

CREATE TYPE utasítással létrehozott beágyazott tábla esetén tiltottak még az alábbi típusok is:

BINARY_INTEGER

LONG RAW

POSITIVEN

PLS_INTEGER

NATURAL

SIGNTYPE

BOOLEAN

NATURALN

STRING

LONG

POSITIVE

 

Egy kollekció elemére a következő módon hivatkozhatunk:

kollekciónév(index)

Ha kollekció típusú visszatérési értékkel rendelkező függvényt használunk, akkor a hivatkozás

alakja:

függvénynév(aktuális_paraméter_lista)(index)

A PL/SQL lehetővé teszi olyan kollekciók használatát is, amelyeknek elemei kollekció típusúak.

1. példa (Kollekciótípusok és ilyen típusú változók deklarációja)

DECLARE
  /* Kollekciót nem tartalmazó kollekciók */
  -- asszociatív tömb, BINARY_INTEGER index
  TYPE t_kolcsonzesek_at_binint IS
    TABLE OF kolcsonzes%ROWTYPE
    INDEX BY BINARY_INTEGER;
  -- asszociatív tömb, PLS_INTEGER index,
  -- szerkezete egyezik az előző típussal, de nem azonos típus
  TYPE t_kolcsonzesek_at_plsint IS
    TABLE OF kolcsonzes%ROWTYPE
    INDEX BY PLS_INTEGER;
  -- asszociatív tömb, VARCHAR2 index, nem rekord típusú elemek
  TYPE t_konyv_idk_at_vc2 IS
    TABLE OF konyv.id%TYPE
    INDEX BY konyv.isbn%TYPE; -- VARCHAR2(30)
  -- beágyazott tábla
  TYPE t_kolcsonzesek_bt IS
    TABLE OF kolcsonzes%ROWTYPE;
  -- dinamikus tömb, objektumot tartalmaz
  -- megj.: Hasonlítsa össze a T_Konyvek adatbázistípussal
  TYPE t_konyvlista IS
    VARRAY(10) OF T_Tetel;
  -- Nem rekordot tartalmazó kollekció
  TYPE t_vektor IS TABLE OF NUMBER
    INDEX BY BINARY_INTEGER;
  /* Kollekciók kollekciói */
  -- mátrix, mindkét dimenziójában szétszórt!
  TYPE t_matrix IS TABLE OF t_vektor
    INDEX BY BINARY_INTEGER;
  -- a konyv tábla egyik oszlopa kollekció
  TYPE t_konyvek_bt IS TABLE OF konyv%ROWTYPE;
  /* Változódeklarációk és inicializációk */
  -- segédváltozók
  v_Tetel T_Tetel;
  v_Szam NUMBER;
  v_Szerzo VARCHAR2(50);
  v_ISBN konyv.isbn%TYPE;
  -- asszociatív tömböket nem lehet inicializálni
  v_Kolcsonzesek_at_binint1 t_kolcsonzesek_at_binint;
  v_Kolcsonzesek_at_binint2 t_kolcsonzesek_at_binint;
  v_Kolcsonzesek_at_plsint t_kolcsonzesek_at_plsint;
  v_Konyv_id_by_ISBN t_konyv_idk_at_vc2;
  v_Vektor t_vektor;
  v_Matrix t_matrix;
  -- Ha nincs inicializáció, akkor a beágyazott tábla és a
  -- dinamikus tömb NULL kezdőértéket kap
  v_Konyvek_N t_konyvek_bt;
  v_Konyvlista_N t_konyvlista;
  -- beágyazott táblát és dinamikus tömböt lehet inicializálni
  -- Megj.: v_Konyvlista_I 2 elemű lesz az inicializálás után
  v_Konyvek_I t_konyvek_bt := t_konyvek_bt();
  v_Konyvlista_I t_konyvlista := t_konyvlista(T_Tetel(10, SYSDATE),
  T_Tetel(15, SYSDATE));
  -- Lehet adatbázisbeli kollekciót is használni
  v_Konyvek_ab T_Konyvek := T_Konyvek();
  -- Ha a tábla elemei skalár típusúak, akkor az inicializálás:
  v_Szerzok_ab T_Szerzok := T_Szerzok('P. Howard', NULL, 'Rejtő Jenő');
  /* Rekordelemű (nem objektum és nem skalár)
  kollekció is inicializálható megfelelő elemekkel.
  Megj.: rekord változó NEM lehet NULL, ha inicializációnál NULL-t
  adunk meg, akkor az adott elem minden mezője NULL lesz. */
  -- Megfelelő rekordtípusú elemek
  v_Kolcsonzes1 kolcsonzes%ROWTYPE;
  FUNCTION a_kolcsonzes(
    p_Kolcsonzo kolcsonzes.kolcsonzo%TYPE,
    p_Konyv kolcsonzes.konyv%TYPE
  ) RETURN kolcsonzes%ROWTYPE;
  -- Az inicializáció
  v_Kolcsonzesek_bt t_kolcsonzesek_bt
    := t_kolcsonzesek_bt(v_Kolcsonzes1, a_kolcsonzes(15, 20));
  -- A függvény implementációja
  FUNCTION a_kolcsonzes(
    p_Kolcsonzo kolcsonzes.kolcsonzo%TYPE,
    p_Konyv kolcsonzes.konyv%TYPE
  ) RETURN kolcsonzes%ROWTYPE IS
  v_Kolcsonzes kolcsonzes%ROWTYPE;
  BEGIN
    SELECT * INTO v_Kolcsonzes
    FROM kolcsonzes
    WHERE kolcsonzo = p_Kolcsonzo
      AND konyv = p_Konyv;
    RETURN v_Kolcsonzes;
  END a_kolcsonzes;
  /* Kollekciót visszaadó függvény */
  FUNCTION fv_kol(p_Null_legyen BOOLEAN) RETURN t_konyvlista IS
  BEGIN
    RETURN CASE
      WHEN p_Null_legyen THEN NULL
      ELSE v_Konyvlista_I
      END;
  END fv_kol;
  ⋮

2. példa (Kollekcióelemre történő hivatkozások (az előző blokk folytatása))


  BEGIN
    /* hivatkozás kollekció elemére: */
    DBMS_OUTPUT.PUT_LINE(v_Kolcsonzesek_bt(2).kolcsonzo);
    DBMS_OUTPUT.PUT_LINE(fv_kol(FALSE)(1).konyv_id);
    -- értékadás lekérdezéssel
    SELECT *
    INTO v_Kolcsonzesek_at_binint1(100)
    FROM kolcsonzes
    WHERE ROWNUM <=1;
    -- Értékadás a teljes kollekció másolásával.
    -- Ez költséges művelet, OUT és IN OUT módú paramétereknél
    -- fontoljuk meg a NOCOPY használatát.
    v_Kolcsonzesek_at_binint2 := v_Kolcsonzesek_at_binint1;
    -- A következő értékadás fordítási hibát okozna, mert
    -- a szerkezeti egyezőség nem elég az értékadáshoz:
    --
    -- v_Kolcsonzesek_at_plsint := v_Kolcsonzesek_at_binint1;
    --
    -- a v_ISBN segédváltozó inicializálása
    SELECT isbn
    INTO v_ISBN
    FROM konyv
    WHERE cim like 'A teljesség felé';
    -- értékadás karakteres indexű asszociatív tömb egy elemének
    SELECT id
    INTO v_Konyv_id_by_ISBN(v_ISBN)
    FROM konyv
    WHERE isbn = v_ISBN;
    -- elem hivatkozása
    DBMS_OUTPUT.PUT_LINE('ISBN: "' || v_ISBN
      || '", id: ' || v_Konyv_id_by_ISBN(v_ISBN));
    -- A v_Matrix elemeit sakktáblaszerűen feltöltjük.
    <<blokk1>>
    DECLARE
      k PLS_INTEGER;
    BEGIN
      k := 1;
      FOR i IN 1..8 LOOP
        FOR j IN 1..4 LOOP
          v_Matrix(i)(j*2 - i MOD 2) := k;
          k := k + 1;
        END LOOP;
      END LOOP;
    END blokk1;
    ⋮