Kurzorok, kurzorváltozók

Hasonló dokumentumok
Együttes hozzárendelés

Adatbázisok-I. előadás dr. Hajas Csilla (ELTE IK)

B IT MAN 65/1. Adatbázis Rendszerek II. Ellenőrző kérdések APLSQL B IT MAN. v:

PL/SQL (folytatás) Kurzorok, függvények, eljárások

8-9.előadás: Adatbázisok-I. dr. Hajas Csilla (ELTE IK)

Kalmár György Adatbázis alapú rendszerek

Tankönyvben: SQL/PSM Gyakorlaton: Oracle PL/SQL

Tranzakciókezelés PL/SQL-ben

Csomag. Adatbázis-objektum Programozási eszközök gyűjteménye Két részből áll. specifikáció törzs (opcionális)

Összefoglaló. <variable_name> [IN OUT IN OUT] <data_type> [:=<default_value>] ... <label_name>: <statements>... GOTO <label_name>;...

Haladó DBMS ismeretek 1

Adatbázis Rendszerek II. 4. PLSQL Kurzorok, hibakezelés 53/1B IT MAN

Triggerek. Olyan névvel ellátott adatbázisobjektumok, amelyek eseményorientált feldolgozást tesznek lehetővé

Adatbázis Rendszerek II. 5. PLSQL Csomagok 16/1B IT MAN

A trigger egy aktualizálási művelet esetén végrehajtandó programrészletet definiál. Alakja:

Kivételkezelés 2. SQLCODE lehetséges értékei:

A PL/SQL motor. A PL/SQL nyelvű egységek fordítását és futtatását végző rendszer. az adatbázis-kezelőben fejlesztőeszközben (pl.

Adatok szűrése, rendezése

Összetett típusok Rekordtípus

Adatbázis használat I. 5. gyakorlat

PL/SQL feladatok 8. gyakorlat

Adattípusok. Max. 2GByte

Adattípusok. Max. 2GByte

PL/SQL blokk. [címke] [DECLARE deklarációs utasítás(ok)] BEGIN végrehajtható utasítás(ok) [EXCEPTION kivételkezelő] END [név];

SQL*Plus. Felhasználók: SYS: rendszergazda SCOTT: demonstrációs adatbázis, táblái: EMP (dolgozó), DEPT (osztály) "közönséges" felhasználók

SQL/PSM kurzorok rész

Kilencedik témakör: Lazarus-Firebird. Készítette: Dr. Kotsis Domokos

PL/SQL 1. rész. Procedural Language extension to SQL

Táblakezelés: Open SQL Internal table. Tarcsi Ádám: Az SAP programozása 1.

Adatbázis Rendszerek II. 6. PLSQL Triggerek 32/1B IT MAN

Adatbázis Rendszerek I. 10. SQL alapok (DML esettanulmány)

Adatbázisban tárolt kollekciók

Adatbázisok* tulajdonságai

Adatbázis kezelés Delphiben. SQL lekérdezések

ORACLE. SYS: rendszergazda SCOTT: demonstrációs adatbázis, táblái: EMP (dolgozó), DEPT (osztály) "közönséges" felhasználók

Adatbázis Rendszerek II. 2. Ea: Gyakorló környezet

Adatbázis Rendszerek II. 8. Gyakorló környezet

Adatbázis-kezelés. Harmadik előadás

Tábla létrehozása: CREATE TABLE alma( ID INT( 3 ) NOT NULL PRIMARY KEY, Leiras VARCHAR( 100 ) );

Az SQL*Plus használata

BEVEZETÉS Az objektum fogalma

B I T M A N B I v: T M A N

B I T M A N B I v: T M A N

Kliens oldali SQL-API

8. Gyakorlat SQL. DDL (Data Definition Language) adatdefiníciós nyelv utasításai:

Adatbázis Rendszerek II. 1. SQL programozási felületek 39/1B IT MAN

Adatbázis használat I. 2. gyakorlat

Adatbázis Rendszerek II. 2. Gyakorló környezet

Adatbázisok. 8. gyakorlat. SQL: CREATE TABLE, aktualizálás (INSERT, UPDATE, DELETE), SELECT október október 26. Adatbázisok 1 / 17

Database Systems II. ZH összefoglaló

Tartalomjegyzék. Tartalomjegyzék 1. Az SQL nyelv 1 Az SQL DDL alapjai 2

5. téma XML DB. Az adatkezelés és XML kapcsolata. Miért fontos az XML használata az adatbázis kezelésben?

Több tábla összekapcsolásán alapuló lekérdezések

Adatbázis Rendszerek II. 3. PLSQL alapok 92/1B IT MAN

Sapientia - Erdélyi Magyar TudományEgyetem (EMTE) ABR 2( Adatbázisrendszerek 2) 3. Előadás: Tárolt eljárások (folytatás) Nézetek

SQL haladó. Külső összekapcsolások, Csoportosítás/Összesítés, Beszúrás/Törlés/Módosítás, Táblák létrehozása/kulcs megszorítások

Lekérdezések az SQL SELECT utasítással

Adatbázis rendszerek SQL nyomkövetés

B I T M A N B I v: T M A N

SQL ALAPOK. Bevezetés A MYSQL szintaxisa Táblák, adatok kezelésének alapjai

Több tábla összekapcsolásán alapuló lekérdezések. Copyright 2004, Oracle. All rights reserved.

Jegyz könyv. Adatbázis-rendszerek II. Beadandó feladat. Miskolci Egyetem

Java és web programozás

Debreceni Egyetem Informatikai Kar TANULÓI NYILVÁNTARTÓ SZOFTVER FIREBIRD ADATBÁZIS ALKALMAZÁSÁVAL

Hatékony PL/SQL programok írása

Adatbázisok-1 előadás Előadó: dr. Hajas Csilla

Adatbázis használat I. 2. gyakorlat

SQL parancsok feldolgozása

Hozzunk ki többet abból amink van. Fehér Lajos

SQL. 1.rész. 1.elıadás // Adatbázisok-1 elıadás // Ullman-Widom (Stanford) tananyaga alapján // Hajas Csilla (ELTE IK) 1

Adatbázis Rendszerek II. 4. Ea: MySQL Tárolt eljárások 110/1 B IT MAN

Objektum-relációs adatbázisok. Felhasználói típusok (User-Defined Types) Objektum ID-k Beágyazott táblák (Nested Tables)

2007. február 25. TARTALOM

Bevezetés: az SQL-be

Bevezetés: Relációs adatmodell

Adatbázis használata PHP-ből

SQLServer. DB Recovery modes

Adatbázisok I. Definíció: DDL: - objektum létrehozás CREATE - objektum megszüntetés DROP - objektum módosítás ALTER

SQL. Táblák összekapcsolása lekérdezéskor Aliasok Allekérdezések Nézettáblák

ADATBÁZIS-KEZELÉS FÉLÉVES FELADAT

Gyakorlás: Hozzunk létre egy Alkalmazottak táblát AZO szám, Részleg szöveg, Munkakör szöveg és BelépésDátuma dátum típussal.

SQL DDL-2 (aktív elemek) triggerek

Többtáblás lekérdezések megjelenítése

SQL OO elemei aktív komponensek

B I T M A N B I v: T M A N

1.1. Feladat Listázza ki a 20-as részleg dolgozóinak nevét, belépési idejét, foglalkozását a nevek szerint csökkenően rendezve.

Adatbázisok II. Jánosi-Rancz Katalin Tünde 327A 1-1

Az SQL adatbázisnyelv: DML

Készítette: Szabóné Nacsa Rozália

SQL DDL-1: táblák és megszorítások

Informatikai képzés Információs rendszerek dr. Hajas Csilla (ELTE IK)

SQL PÉLDATÁR. készült a PTE TTK Iskolai informatika III. kurzus teljesítésére

Bevezetés az SQL-be. Tankönyv: Ullman-Widom: Adatbázisrendszerek Alapvetés Második, átdolgozott kiadás, Panem, 2009

Lekérdezések az SQL SELECT utasítással

Adatbázisok. 9. gyakorlat SQL: SELECT október október 26. Adatbázisok 1 / 14

ADATBÁZISOK gyakorlat: SQL 2. rész SELECT

Tankönyv: SQL/PSM + Gyak:PL/SQL

Lekérdezések az SQL SELECT utasítással. Copyright 2004, Oracle. All rights reserved.

Adatbázisok II. Jánosi-Rancz Katalin Tünde 327A

Adatbázisok webalkalmazásokban

Átírás:

Kurzorok, kurzorváltozók

Kurzorok Környezeti terület: memóriaterület információ a kezelt sorokról lekérdezés esetén aktív halmaz mutató az utasítás belső reprezentációjára stb. Kurzorral nevesítjük explicit implicit (rejtett)

Explicit kurzor Életciklus: deklaráció CURSOR név[(paraméter[,paraméter] )] [RETURN sortípus] IS select_utasítás; megnyitás OPEN kurzornév[(aktuális_paraméterlista)]; sorok betöltése FETCH {kurzornév kurzorváltozónév} {INTO {rekordnév változónév[,változónév] } BULK COLLECT INTO kollekciónév[,kollekciónév] LIMIT sorok}; lezárás CLOSE {kurzornév kurzorváltozónév};

CURSOR cur_ugyfelek RETURN ugyfel%rowtype IS SELECT * FROM ugyfel ORDER BY UPPER(nev); v_uid ugyfel.id%type; CURSOR cur_ugyfel1 IS SELECT nev, tel_szam FROM ugyfel WHERE id = v_uid; CURSOR cur_ugyfel2(p_uid ugyfel.id%type DEFAULT v_uid) IS SELECT * FROM ugyfel WHERE id = p_uid;

DECLARE CURSOR c2 IS SELECT * FROM employees WHERE job_id LIKE '%MGR' OR job_id LIKE '%MAN' ORDER BY job_id; v_employees employees%rowtype; OPEN c2; LOOP FETCH c2 INTO v_employees; EXIT WHEN c2%notfound; DBMS_OUTPUT.PUT_LINE( v_employees.last_name v_employees.job_id ); END LOOP; CLOSE c2; /

DECLARE CURSOR c IS SELECT e.job_id, j.job_title FROM employees e, jobs j WHERE e.job_id = j.job_id AND e.manager_id = 100 ORDER BY last_name; job1 c%rowtype; job2 c%rowtype; job3 c%rowtype; OPEN c; FETCH c INTO job1; -- fetches first row FETCH c INTO job2; -- fetches second row FETCH c INTO job3; -- fetches third row CLOSE c; DBMS_OUTPUT.PUT_LINE(job1.job_title ' ' job1.job_id); DBMS_OUTPUT.PUT_LINE(job1.job_title ' ' job2.job_id); DBMS_OUTPUT.PUT_LINE(job1.job_title ' ' job3.job_id); /

Kurzorattribútumok %FOUND megnyitás után, de első betöltés előtt NULL, sikeres betöltés esetén TRUE, egyébként FALSE. %ISOPEN ha meg van nyitva, TRUE, egyébként FALSE. %NOTFOUND %FOUND negáltja. %ROWCOUNT megnyitás után, de első betöltés előtt 0, minden sikeres betöltés esetén eggyel nő.

DECLARE CURSOR c1 IS SELECT last_name, salary FROM employees WHERE ROWNUM < 11; the_name employees.last_name%type; the_salary employees.salary%type; IF NOT c1%isopen THEN OPEN c1; END IF; FETCH c1 INTO the_name, the_salary; IF c1%isopen THEN CLOSE c1; END IF; /

DECLARE CURSOR c1 IS SELECT last_name, salary FROM employees WHERE ROWNUM < 11 ORDER BY last_name; my_ename employees.last_name%type; my_salary employees.salary%type; OPEN c1; LOOP FETCH c1 INTO my_ename, my_salary; IF c1%found THEN -- fetch succeeded DBMS_OUTPUT.PUT_LINE(my_ename ', ' my_salary); ELSE EXIT; -- fetch failed END IF; END LOOP; /

DECLARE CURSOR c1 IS SELECT last_name FROM employees WHERE ROWNUM < 11 ORDER BY last_name; name employees.last_name%type; OPEN c1; LOOP FETCH c1 INTO name; EXIT WHEN c1%notfound OR c1%notfound IS NULL; DBMS_OUTPUT.PUT_LINE(c1%ROWCOUNT '. ' name); IF c1%rowcount = 5 THEN DBMS_OUTPUT.PUT_LINE('--- Fetched 5th row ---'); END IF; END LOOP; CLOSE c1;

Kivételek CURSOR_ALREADY_OPEN: megnyitott kurzor újra megnyitásakor INVALID_CURSOR: nem megnyitott kurzoron végzett FETCH/CLOSE vagy kurzorattribútumhivatkozás (kivéve az ISOPEN-t) esetén

DECLARE CURSOR c IS SELECT * FROM HR.EMPLOYEES; v c%rowtype; --FETCH c INTO v; OPEN c; --OPEN c; LOOP FETCH c INTO v; EXIT WHEN c%notfound; DBMS_OUTPUT.PUT_LINE( Név: v.first_name v.last_name ; Fizetés: v.salary); END LOOP; CLOSE c; --CLOSE c; EXCEPTION WHEN INVALID_CURSOR THEN DBMS_OUTPUT.PUT_LINE( Nem volt megnyitva! ); WHEN CURSOR_ALREADY_OPEN THEN DBMS_OUTPUT.PUT_LINE( Már nyitva volt! ); /

DECLARE CURSOR cur_ugyfel2(p_uid ugyfel.id%type DEFAULT v_uid) IS SELECT * FROM ugyfel v_ugyfel WHERE id = p_uid; v_uid := 15; OPEN cur_ugyfel2(15); LOOP ugyfel%rowtype; FETCH cur_ugyfel2 INTO v_ugyfel; EXIT WHEN cur_ugyfel2%notfound; DBMS_OUTPUT.PUT_LINE(v_Ugyfel.nev); END LOOP; CLOSE cur_ugyfel2;

SELECT FOR UPDATE A lekérdezés által érintett rekordok explicit zárolása egy későbbi UPDATE vagy DELETE miatt biztosítjuk, hogy más ne módosíthasson az adatokon, amíg a tranzakciónk be nem fejeződik Ilyenkor az UPDATE ill. DELETE tartalmazhat egy WHERE CURRENT OF utasításrészt, amellyel a legutóbbi FETCH által betöltött sor módosítható, ill. törölhető

DECLARE CURSOR c1 IS SELECT empno, job, sal FROM emp FOR UPDATE; v_c1 c1%rowtype; c_job emp.job%type:='manager'; OPEN c1; LOOP FETCH c1 INTO v_c1; exit when c1%notfound; if v_c1.job=c_job and v_c1.sal>5000 then UPDATE emp SET sal = new_sal WHERE CURRENT OF c1; end if; END LOOP; CLOSE c1;

Implicit kurzor Minden DML utasításhoz, amelyhez nem tartozik explicit kurzor, felépül egy implicit Neve: SQL Attribútumai: %FOUND: TRUE, ha az utasítás legalább egy sort érintett, különben FALSE; %ISOPEN: mindig FALSE; %NOTFOUND: a FOUND negáltja; %ROWCOUNT: kezelt sorok darabszáma (kivéve SELECT INTO); %BULK_ROWCOUNT: asszociatív tömb, FORALL használata esetén i indexű eleme megadja, hogy az erre lefuttatott DML utasítás hány sort kezelt; %BULK_EXCEPTIONS: <ERROR_INDEX, ERROR_CODE> felépítésű rekordok asszociatív tömbje a FORALL működése közben bekövetkezett kivételek kezelésére.

UPDATE employee SET salary = 100 WHERE id = '01'; IF SQL%ROWCOUNT = 0 THEN INSERT INTO employee (id, salary) VALUES ('99', 100); DBMS_OUTPUT.put_line('SQL%ROWCOUNT = 0'); END IF; /

Kurzor FOR ciklus FOR ciklusváltozó IN {kurzornév[(aktuális_paraméterlista)] (select_utasítás)} LOOP utasítás [,utasítás] END LOOP; Implicit módon kurzornév%rowtype típusú lesz a ciklusváltozó Megnyitja a kurzort Betölti az aktív halmaz összes sorát Lezárja a kurzort Ha explicit kurzor helyett egy SELECT utasítást adunk meg, akkor egy rejtett kurzor jön létre

DECLARE CURSOR c(p HR.EMPLOYEES.FIRST_NAME%TYPE) IS SELECT department_name FROM HR.DEPARTMENTS WHERE department_id IN (SELECT department_id FROM HR.EMPLOYEES WHERE first_name=p); FOR nevek IN (SELECT DISTINCT first_name FROM hr.employees) LOOP DBMS_OUTPUT.PUT_LINE(nevek.first_name : ); FOR i IN c(nevek.first_name) LOOP DBMS_OUTPUT.PUT_LINE( i.department_name); END LOOP; DBMS_OUTPUT.PUT_LINE; END LOOP; /

DECLARE CURSOR c1 (job VARCHAR2, max_wage NUMBER) IS SELECT * FROM employees WHERE job_id = job AND salary > max_wage; FOR person IN c1('st_clerk', 3000) LOOP -- process data record DBMS_OUTPUT.PUT_LINE ( 'Name = ' person.last_name ', salary = ' person.salary ', Job Id = ' person.job_id); END LOOP; /

FOR item IN ( SELECT first_name ' ' last_name AS full_name, LOOP FROM employees salary * 10 AS dream_salary WHERE ROWNUM <= 5 ORDER BY dream_salary DESC, last_name ASC) DBMS_OUTPUT.PUT_LINE (item.full_name ' dreams of making item.dream_salary); END LOOP; /

Kurzorváltozók 1 Nem kell fordítási időben ismerni a kurzorhoz kapcsolt SELECT-et Referencia típusú változó, amely mindig a hivatkozott sor címét tartalmazza Létrehozása két lépésben történik REF CURSOR típus létrehozása TYPE név IS REF CURSOR [RETURN {{táblanév kurzornév kurzorváltozónév}%rowtype rekordnév%type rekordtípusnév kurzorreferenciatípus_név}]; kurzorváltozó deklarálása v_refcursor t_refcursor;

Kurzorváltozók 2 Kurzorreferencia típus lehet erős (ha szerepel a RETURN rész): a fordító ellenőrzi a kapcsolt lekérdezés típuskompatibilitását gyenge (különben): bármely lekérdezés hozzákapcsolható ilyen típusú kurzorváltozóhoz előre definiált típus: SYS_REFCURSOR Életciklus ugyanaz, mint az explicit kurzoroknál, de megnyitás OPEN-FOR utasítással akárhányszor megnyitható lezárás nélkül! OPEN kurzorváltozó_név FOR select_utasítás; Kurzorreferencia típus lehet alprogram formális paramétere is

DECLARE TYPE t_eros_dept IS REF CURSOR RETURN hr.departments%rowtype; v_refc SYS_REFCURSOR; v_eros t_eros_dept; v_emp_rec hr.employees%rowtype; v_datum DATE; v_felh_nev VARCHAR2(20); OPEN v_refc FOR SELECT * FROM hr.employees WHERE salary>15000; -- fordítási hiba: -- OPEN v_eros FOR SELECT * FROM hr.employees; LOOP FETCH v_refc INTO v_emp_rec; EXIT WHEN v_refc%notfound; DBMS_OUTPUT.PUT_LINE(v_refc.employee_id); END LOOP; OPEN v_refc FOR SELECT SYSDATE, USER FROM DUAL; FETCH v_refc INTO v_datum, v_felh_nev; DBMS_OUTPUT.PUT_LINE(v_felh_nev v_datum); CLOSE v_refc; /

CREATE FUNCTION f(cur SYS_REFCURSOR, mgr_hiredate DATE) RETURN NUMBER IS emp_hiredate DATE; before number :=0; after number:=0; begin loop fetch cur into emp_hiredate; exit when cur%notfound; if emp_hiredate > mgr_hiredate then after:=after+1; else before:=before+1; end if; end loop; close cur; if before > after then return 1; else return 0; end if; end; /

DECLARE TYPE t_egyed IS RECORD (id NUMBER, leiras VARCHAR2(100)); TYPE t_ref_egyed IS REF CURSOR RETURN t_egyed; v_refcursor v_egyedek1 v_egyedek2 v_egyed SYS_REFCURSOR; t_ref_egyed; t_ref_egyed; t_egyed; PROCEDURE megnyit_konyv (p_cursor IN OUT SYS_REFCURSOR) IS OPEN p_cursor FOR SELECT id, cim FROM konyv;

FUNCTION megnyit_ugyfel RETURN t_ref_egyed IS rv t_ref_egyed; OPEN rv FOR SELECT id, nev FROM ugyfel; RETURN rv; FUNCTION betolt(p_cursor IN t_ref_egyed) RETURN t_egyed IS rv t_egyed; FETCH p_cursor INTO rv; RETURN rv;

PROCEDURE bezar(p_cursor IN SYS_REFCURSOR) IS IF p_cursor%isopen THEN CLOSE p_cursor; END IF; megnyit_konyv(v_egyedek1); v_refcursor := megnyit_ugyfel; 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);

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); 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!');

OPEN v_refcursor FOR SELECT 'alma',2,3,4 FROM DUAL; v_egyedek2 := v_refcursor; EXCEPTION WHEN OTHERS THEN CLOSE v_refcursor; RAISE; /

CURSOR kifejezések Beágyazott kérdés sorait kezeljük kurzorral Az aktív halmaz soraiban értékek és kurzorok vannak A beágyazott kurzor implicit nyílik meg a szülő kurzor sorának betöltésekor Lezáródik, ha explicit módon bezárjuk új sort tölt be vagy lezáródik a szülő hiba történik a szülőbe betöltéskor (clean-up)

CURSOR kifejezésekre vonatkozó megszorítások Ha a tartalmazó utasítás SELECT, akkor beágyazott kurzorok megjelenhetnek a legkülső SELECT-listán, vagy egy másik beágyazott kurzor legkülső SELECT-listáján is Beágyazott kurzorok nem szerepelhetnek nézetekben

declare begin for dep in (select department_id, department_name from hr.departments where department_name in ('Executive,'Marketing' ) order by department_name) loop Dbms_Output.Put_Line ( department.department_name ); for employee in (select last_name from hr.employees where department_id = dep.department_id order by last_name) loop Dbms_Output.Put_Line ( employee.last_name ); end loop; end loop; end; /

declare cursor the_departments is select department_name, cursor (select last_name from hr.employees e where e.department_id = d.department_id order by last_name) from hr.departments d where department_name in ( 'Executive', 'Marketing' ) order by department_name; v_department_name hr.departments.department_name%type; the_employees sys_refcursor; v_employee_last_name hr.employees.last_name%type; begin open the_departments; loop fetch the_departments into v_department_name, the_employees; exit when the_departments%notfound; Dbms_Output.Put_Line ( v_department_name ); loop fetch the_employees into v_employee_last_name; exit when the_employees%notfound; Dbms_Output.Put_Line ( v_employee_last_name ); end loop; end loop; close the_departments; end; /

select a.table_name, cursor(select column_name from user_tab_columns b where b.table_name = a.table_name) col_list from user_tables a

DECLARE sal employees.salary%type; sal_multiple employees.salary%type; factor INTEGER := 2; CURSOR c1 IS SELECT salary, salary*factor FROM employees WHERE job_id LIKE 'AD_%'; OPEN c1; -- PL/SQL evaluates factor LOOP FETCH c1 INTO sal, sal_multiple; EXIT WHEN c1%notfound; DBMS_OUTPUT.PUT_LINE('factor=' factor); DBMS_OUTPUT.PUT_LINE('sal=' sal); DBMS_OUTPUT.PUT_LINE('sal_multiple=' sal_multiple); factor := factor + 1; -- Does not affect sal_multiple END LOOP; CLOSE c1; /

DECLARE CURSOR c(job VARCHAR2,max_sal NUMBER) IS SELECT last_name,first_name,(salary - max_sal) overpayment FROM employees WHERE job_id = job AND salary > max_sal ORDER BY salary; PROCEDURE print_overpaid IS last_name_ employees.last_name%type; first_name_ employees.first_name%type; overpayment_ employees.salary%type; LOOP FETCH c INTO last_name_, first_name_, overpayment_; EXIT WHEN c%notfound; DBMS_OUTPUT.PUT_LINE(last_name_ ', ' first_name_ ' (by ' overpayment_ ')'); END LOOP; END print_overpaid;

DBMS_OUTPUT.PUT_LINE('Overpaid Stock Clerks:'); OPEN c('st_clerk', 5000); print_overpaid; CLOSE c; DBMS_OUTPUT.PUT_LINE('Overpaid Sales Representatives:'); OPEN c('sa_rep', 10000); print_overpaid; CLOSE c; /