You are on page 1of 48

UNINOVE Programaco para Banco de Dados Prof MsC Marcelo Bianchi

Cursores: Implcitos e Explcitos


AULA 06

Cursores
A fim de processar uma instruo SQL, o Oracle alocar uma rea de memria conhecida como rea de contexto. A rea de contexto contm as informaes necessrias para completar o processamento,incluindo o numero de linhas processadas pela instruo, e no caso de uma consulta, o conjunto ativo, que o conjunto de linhas retornado pela consulta.

DECLARE /* Output variables to hold the results of the query */ v_StudentID students.id%TYPE; v_FirstName students.first_name%TYPE; v_LastName students.last_name%TYPE;
/* Bind variable used in the query */ v_Major students.major%TYPE := 'Computer Science'; /* Cursor declaration */ CURSOR c_Students IS SELECT id, first_name, last_name FROM students WHERE major = v_Major;

BEGIN /* Identify the rows in the active set, and prepare for further processing of the data */ OPEN c_Students; LOOP /* Retrieve each row of the active set into PL/SQL variables */ FETCH c_Students INTO v_StudentID, v_FirstName, v_LastName; /* If there are no more rows to fetch, exit the loop */ EXIT WHEN c_Students%NOTFOUND;

END LOOP;

/* Free resources used by the query */ CLOSE c_Students; END;


Cursores so trechos alocados de memria destinados a processar as declaraes SELECT. Podem ser definidos pelo prprio PL/SQL, chamados de Cursores Implcitos, ou podem ser definidos manualmente, so os chamados de Cursores Explcitos.

Cursores explcitos Os cursores explcitos so chamados dessa forma porque so declarados formalmente na rea de declaraes do mdulo, ao contrrio do que ocorre com os cursores implcitos. So tipicamente mais flexveis e poderosos que os cursores implcitos, podendo substitu-los em qualquer situao. Para sua utilizao so necessrios alguns passos bsicos: declarar o cursor ; declarar as variveis que recebero os dados ; abrir (uma espcie de preparao) o cursor na rea de instrues ; ler os dados produzidos pelo cursor ; fechar (desalocar a memria) do cursor.

O exemplo a seguir ilustra a utilizao de um cursor explcito equivalente quele utilizado para demonstrar o cursor implcito. Analise as principais diferenas entre essa soluo e a anterior. Inicialmente, existe a declarao do cursor nas linhas 004 a 007, onde no aparece a clusula INTO. O nome dado ao cursor segue as mesmas regras para os nomes de variveis. A sintaxe bsica dessa declarao a seguinte:

CURSOR <nome do cursor> IS <declarao SELECT> ;

Ao fazermos a declarao, apenas foi definida uma estrutura que ser utilizada posteriormente, quando o SELECT for executado e a recuperao das linhas for realizada. A primeira instruo realmente executvel relativa ao cursor a sua abertura, feita na rea de instrues atravs do comando OPEN. no momento da abertura que o SELECT executado e as linhas recuperadas tornam-se disponveis para uso. O exemplo a seguir ilustra a utilizao de um cursor explcito equivalente quele utilizado para demonstrar o cursor implcito. Analise as principais diferenas entre essa soluo e a anterior. Inicialmente, existe a declarao do cursor nas linhas 004 a 007, onde no aparece a clusula INTO. O nome dado ao cursor segue as mesmas regras para os nomes de variveis.

A sintaxe bsica dessa declarao a seguinte:


CURSOR <nome do cursor> IS <declarao SELECT> ; Ao fazermos a declarao, apenas foi definida uma estrutura que ser utilizada posteriormente, quando o SELECT for executado e a recuperao das linhas for realizada. A primeira instruo realmente executvel relativa ao cursor a sua abertura, feita na rea de instrues atravs do comando OPEN. no momento da abertura que o SELECT executado e as linhas recuperadas tornam-se disponveis para uso.

A sintaxe do comando OPEN : OPEN <nome-do-cursor>;

DECLARE v_StudentID students.ID%TYPE; CURSOR c_AllStudentIDs IS SELECT ID FROM students; BEGIN OPEN c_AllStudentIDs;

-- Open it again. This will raise ORA-6511. OPEN c_AllStudentIDs; END;

Para obtermos as linhas que foram recuperadas pela consulta, devemos busc-las, uma a uma, na estrutura do cursor e copiar seus dados para as variveis correspondentes. Isso obtido atravs do comando FETCH:
FETCH <nome-do-cursor> INTO <lista de variveis> ;

DECLARE v_Department classes.department%TYPE; v_Course classes.course%TYPE; CURSOR c_AllClasses IS SELECT * FROM classes; v_ClassesRecord c_AllClasses%ROWTYPE; BEGIN OPEN c_AllClasses;
-- This is a legal FETCH statement, returning the first -- row into a PL/SQL record which matches the select list -- of the query. FETCH c_AllClasses INTO v_ClassesRecord;

-- This FETCH statement is illegal, since the select list -- of the query returns all 7 columns in the classes table -- but we are only fetching into 2 variables. -- This will raise the error "PLS-394: wrong number of values -- in the INTO list of a FETCH statement". FETCH c_AllClasses INTO v_Department, v_Course; END;

Ao final do processamento, o cursor fechado atravs de um comando CLOSE, cuja sintaxe :CLOSE <nome-do-cursor> ;

Alm dos recursos bsicos ligados aos cursores, existem trs outros que merecem ateno: a possibilidade da passagem de parmetros para os cursores; os chamados atributos de cursor e cursores explcitos via looping FOR.

Parmetros de cursor

Geralmente o comando SELECT de um cursor possui uma clusula WHERE que especifica uma seleo de linhas a serem retornadas. Muitas vezes, temos necessidade de variar um dado a ser comparado nessa clusula, e isso pode ser feito atravs de uma espcie de parmetro passado para o cursor no momento de sua abertura. Observe o exemplo a seguir:

DECLARE
CURSOR c_Classes(p_Department classes.department%TYPE,

p_Course classes.course%TYPE) IS
SELECT * FROM classes

WHERE department = p_Department


AND course = p_Course; BEGIN OPEN c_Classes('HIS', 101); END;

Atributos de cursor

Durante a utilizao de um cursor em uma rotina, uma srie de valores pode ser testada, de maneira a permitir a monitorao do estado corrente do processamento. Esses valores so obtidos atravs de variveis especiais mantidas pelo sistema, chamadas de Atributos do cursor. Todos eles tm seu nome comeando com o smbolo % (sinal de porcentagem) e so referenciados colocando-se o nome do cursor imediatamente antes do %. A seguir um pequeno resumo com esses atributos e suas caractersticas principais.

Atributos: %ISOPEN (BOOLEAN): Indica se o cursor referenciado est aberto (TRUE) ou fechado (FALSE). %ROWCOUNT (NUMBER): um contador que indica quantas linhas j foram recuperadas atravs de um comando FETCH.

%NOTFOUND (BOOLEAN): Indica o resultado do ltimo FETCH: se foi bem sucedido, seu valor FALSE, seno TRUE
%FOUND (BOOLEAN): Indica o resultado do ltimo FETCH: se foi bem sucedido, seu valor TRUE, seno FALSE.

Cursores implcitos Um cursor deste tipo implementado atravs da colocao da clusula INTO no SELECT, conforme pode ser visto no exemplo a seguir: Dois cuidados bsicos devem ser tomados ao utilizar-se cursores implcitos: as variveis que recebero os dados obtidos pelo SELECT devero ser declaradas com tipo igual ao do campo correspondente na tabela, o que torna bastante indicado nesses casos utilizar o atributo %TYPE ao declarar a varivel, para evitar problemas de incompatibilidade; o comando deve retornar no mximo uma nica linha, seno uma exceo TOO_MANY_ROWS ser gerada. No h uma declarao formal do cursor, apenas das variveis a serem atualizadas pelo comando.

Exemplo: BEGIN UPDATE rooms SET number_seats = 100 WHERE room_id = 99980; -- If the previous UPDATE statement didn't match any rows, -- insert a new row into the rooms table. IF SQL%NOTFOUND THEN INSERT INTO rooms (room_id, number_seats) VALUES (99980, 100); END IF; END; ------

BEGIN UPDATE rooms SET number_seats = 100 WHERE room_id = 99980; -- If the previous UPDATE statement didn't match any rows, -- insert a new row into the rooms table. IF SQL%ROWCOUNT = 0 THEN INSERT INTO rooms (room_id, number_seats) VALUES (99980, 100); END IF; END;

Loops de Busca de Cursores A operao mais comum com os cursores buscas todas as linhas no conjunto ativo. Isso feito via um loop de busca, o que simplesmente um loop que processa cada uma das linhas no conjunto ativo, uma por uma.

Loop Simples
DECLARE v_StudentID students.id%TYPE; v_FirstName students.first_name%TYPE; v_LastName students.last_name%TYPE; students CURSOR c_HistoryStudents IS SELECT id, first_name, last_name FROM students WHERE major = 'History'; BEGIN OPEN c_HistoryStudents; LOOP FETCH c_HistoryStudents INTO v_StudentID, v_FirstName, v_LastName; EXIT WHEN c_HistoryStudents%NOTFOUND;

INSERT INTO registered_students (student_id, department, course) VALUES (v_StudentID, 'HIS', 301);
INSERT INTO temp_table (num_col, char_col) VALUES (v_StudentID, v_FirstName || ' ' || v_LastName);

END LOOP;
CLOSE c_HistoryStudents; END; /

Loops WHILE
DECLARE CURSOR c_HistoryStudents IS SELECT id, first_name, last_name FROM students WHERE major = 'History'; v_StudentData c_HistoryStudents%ROWTYPE; BEGIN OPEN c_HistoryStudents; FETCH c_HistoryStudents INTO v_StudentData;

WHILE c_HistoryStudents%FOUND LOOP INSERT INTO registered_students (student_id, department, course) VALUES (v_StudentData.ID, 'HIS', 301);
INSERT INTO temp_table (num_col, char_col) VALUES (v_StudentData.ID, v_StudentData.first_name || ' ' || v_StudentData.last_name); FETCH c_HistoryStudents INTO v_StudentData; END LOOP; CLOSE c_HistoryStudents; END; /

Loops FOR de cursor Esta uma variao do cursor explcito em que este fica embutido em uma estrutura FOR responsvel pelo seu processamento. uma espcie de simplificao, pois nessa estrutura o cursor aberto uma vez, as linhas so processadas (uma a cada passagem do looping) e o cursor fechado automaticamente no final das iteraes. O <nome do registro> um nome de varivel do tipo RECORD criada automaticamente na entrada do looping. O cursor propriamente dito declarado normalmente na rea de declaraes da rotina, e pode prever a utilizao de parmetros. Os atributos de cursor vistos anteriormente esto disponveis normalmente neste tipo de estrutura.

A sintaxe bsica a seguinte: FOR <nome do registro> IN <nome do cursor> LOOP ... ; END LOOP ; Exemplo: DECLARE CURSOR c_HistoryStudents IS SELECT id, first_name, last_name

FROM students WHERE major = 'History'; BEGIN FOR v_StudentData IN c_HistoryStudents LOOP

INSERT INTO registered_students (student_id, department, course) VALUES (v_StudentData.ID, 'HIS', 301);
INSERT INTO temp_table (num_col, char_col) VALUES (v_StudentData.ID, v_StudentData.first_name || ' ' || v_StudentData.last_name); END LOOP;

END; /

Loops FOR implcitos


A sintaxe para um loop FOR pode ser abreviada ainda mais. Alm do registro, o prprio cursor pode ser implicitamente declarado, como o seguinte exemplo ilustra: BEGIN FOR v_StudentData IN (SELECT id, first_name, last_name FROM students WHERE major = 'History') LOOP INSERT INTO registered_students (student_id, department, course) VALUES (v_StudentData.ID, 'HIS', 301);

INSERT INTO temp_table (num_col, char_col) VALUES (v_StudentData.ID, v_StudentData.first_name || ' ' || v_StudentData.last_name); END LOOP; END; A consulta contida dentro de parnteses, dentro da prpria instruo FOR. Nesse caso, tanto o registro v_StudentData como o cursor implicitamente declarado. Entretanto, o cursor no tem nenhum nome. NO_DATA_FOUND versus %NOTFOUND A exceo NO_DATA_FOUND levantada apenas em instrues SELECT... INTO, quando a clusula WHERE da consulta no corresponde a nenhuma linha. Quando nenhuma linha retornada por um cursor explcito ou uma instruo UPDATE ou DELETE, o atributo %NOTFOUND configurado como TRUE, no levantando a exceo NO_DATA_FOUND.

Cursores SELECT FOR UPDATE Frequentemente, o processamento feito em um loop de busca modifica as linhas que foram recuperadas pelo cursor. A PL/SQL fornece uma sintaxe conveniente para fazer isso. Esse mtodo consiste em duas partes: a clusula FOR UPDATE na declarao do cursor e a clusula WHERE CURRENT em uma instruo UPDATE ou DELETE. FOR UPDATE A clusula FOR UPDATE a parte de uma instruo SELECT. Ela vlida como a ltima clusula da instruo, depois da clusula ORDER BY (caso exista). Sintaxe: SELECT ... FROM ... FOR UPDATE [OF colunas] [{NOWAIT | WAIT n}]

Exemplo:

DECLARE
CURSOR c_AllStutends IS SELECT * FROM students

FOR UPDATE OF first_name, last_name;

WHERE CURRENT OF Se o cursor for declarado FOR UPDATE, a clusula WHERE CURRENT OF pode ser utilizada em uma instruo UPDATE ou DELETE para referenciar a linha corrente do cursor. Sintaxe: {UPDATE | DELETE} tabela WHERE CURRENT OF cursor

Exemplo:

DECLARE v_NumCredits classes.num_credits%TYPE;


CURSOR c_RegisteredStudents IS SELECT * FROM students WHERE id IN (SELECT student_id FROM registered_students

WHERE department= 'HIS' AND course = 101) FOR UPDATE OF current_credits; BEGIN FOR v_StudentInfo IN c_RegisteredStudents LOOP SELECT num_credits INTO v_NumCredits FROM classes WHERE department = 'HIS' AND course = 101;

UPDATE students SET current_credits = current_credits + v_NumCredits WHERE CURRENT OF c_RegisteredStudents; END LOOP; COMMIT; END; /

COMMIT dentro de um Loop de cursor FOR UPDATE Quando houver um COMMIT dentro de um loop de cursor SELECT ... FOR UPDATE, quaisquer tentativa de busca aps o COMMIT retornar erro ORA-1002 pois o cursor invalidado aps essa operao para liberar os bloqueios nas linhas das tabelas. Contudo, voc pode utilizar a chave primria da tabela na clusula WHERE de UPDATE, como ilustrado pelo seguinte exemplo:

DECLARE v_NumCredits classes.num_credits%TYPE; CURSOR c_RegisteredStudents IS SELECT * FROM students WHERE id IN (SELECT student_id FROM registered_students WHERE department= 'HIS' AND course = 101);

BEGIN FOR v_StudentInfo IN c_RegisteredStudents LOOP SELECT num_credits INTO v_NumCredits FROM classes WHERE department = 'HIS' AND course = 101; UPDATE students SET current_credits = current_credits + v_NumCredits WHERE id = v_Studentinfo.id; COMMIT; END LOOP; END; /

Variveis de cursor

Uma varivel de cursor um tipo referncia. Um tipo referncia pode referir-se a posies diferentes de memria medida que o programa executado. A sintaxe para definir um tipo de varivel de cursor :
TYPE nome_do_tipo IS REF CURSOR [RETURN tipo_de_retorno] Onde nome_do_tipo o nome do novo tipo de referncia e o tipo_de_retorno um tipo de registro que indica os tipos da lista SELECT que por fim retornao pela varivel cursor.

A fim de associar uma varivel de cursor com uma instruo SELECT em particular, a sintaxe OPEN estendida para permitir que a consulta seja especificada. Veja a sintaxe: OPEN varivel_de_cursor FOR instruo_select;

Exemplo: SQL> set serveroutput on SQL> DECLARE 2 TYPE TMeuCursor IS REF CURSOR; 3 v_Cursor TMeuCursor; 4 v_EmpRec emp%ROWTYPE; 5 v_DeptRec dept%ROWTYPE; 6 BEGIN 7 --Utilizar tabela EMP 8 OPEN v_Cursor FOR select * from emp; 9 FETCH v_Cursor INTO v_EmpRec; 10 DBMS_OUTPUT.PUT_LINE(v_EmpRec.empno||''||v_EmpRec.ename); 11 CLOSE v_Cursor; 12 --Utilizar tabela DEPT 13 OPEN v_Cursor FOR select * from dept; 14 FETCH v_Cursor INTO v_DeptRec;

15 DBMS_OUTPUT.PUT_LINE(v_DeptRec.deptno||''||v_DeptRec.dname); 16 / CLOSE v_Cursor;

17 end;

7369-SMITH
10-ACCOUNTING PL/SQL procedure successfully completed.

O exemplo abaixo uma procedure armazenada que seleciona linhas da tabela classes ou rooms, dependendo da sua entrada.

CREATE OR REPLACE PROCEDURE ShowCursorVariable (p_Table IN VARCHAR2) AS


TYPE t_ClassesRooms IS REF CURSOR; v_CursorVar t_ClassesRooms;

v_Department classes.department%TYPE; v_Course classes.course%TYPE; v_RoomID rooms.room_id%TYPE; v_Description rooms.description%TYPE;

BEGIN IF p_Table = 'classes' THEN OPEN v_CursorVar FOR SELECT department, course FROM classes; ELSIF p_table = 'rooms' THEN OPEN v_CursorVar FOR SELECT room_id, description FROM rooms; ELSE RAISE_APPLICATION_ERROR(-20000, 'Input must be ''classes'' or ''rooms'''); END IF; LOOP IF p_Table = 'classes' THEN FETCH v_CursorVar INTO v_Department, v_Course; EXIT WHEN v_CursorVar%NOTFOUND;
INSERT INTO temp_table (num_col, char_col) VALUES (v_Course, v_Department);

ELSE FETCH v_CursorVar INTO v_RoomID, v_Description; EXIT WHEN v_CursorVAR%NOTFOUND;


INSERT INTO temp_table (num_col, char_col) VALUES (v_RoomID, SUBSTR(v_Description, 1, 60)); END IF; END LOOP;

CLOSE v_CursorVar;
COMMIT; END ShowCursorVariable;

FIM !
E-mail: marcelo.bianchi@uninove.br