Sie sind auf Seite 1von 25

Linguagem Procedural para Banco de Dados

Viso Geral

As caractersticas principais das linguagens procedurais para bancos de dados, tambm


conhecidas por "PLSQL", so:

- Proporcionar a possibilidade da criao de funes ou procedimentos armazenados


(functions/stored procedures) e gatilhos (triggers);
- Adicionar estruturas de controle de fluxo linguagem SQL;
- Herdar e manipular todos os tipos de dados, funes e operadores existentes no banco de
dados;
- Considerada confivel por parte do servidor;
- So de fcil utilizao

As principais vantagens advindas do uso de linguagens procedurais para banco de dados


so:

- Reduo do nmero de "round trips" desnecessrios entre clientes e servidores;


- Possibilidade de agrupar vrios comandos SQL em um nico procedimento;
- Reduo do nmero de "query parsings";
- Aumento da segurana do cdigo;
- Possvel reduo do consumo de rede.

1
Linguagem Procedural

O PostGreSQL fornece quatro tipos de funes:


Query Language Functions (Funes escritas em SQL)
Procedural Language Functions (funes escritas em, por exemplo, PL/pgSQL)
Funes internas;
Funes em linguagem C.

Criando uma Funo

CREATE [or REPLACE] FUNCTION nome_funcao(par_1 tipo, par_2 tipo ... par_n)
RETURNS [SETOF] [VOID || DATATYPE || ESCALAR] AS
$$
DECLARE
BEGIN
RETURN;
END;
$$ LANGUAGE 'PLpgSQL';

SQL Functions

Iniciaremos nosso estudo pelas linguagens de programao escritas em SQL.

- Executam uma lista de comandos SQL;


- Retornam o resultado da ltima consulta da lista;
- Se a ltima consulta no retornar valores um null ser retornado;
- Alternativamente uma funo SQL poder retornar um conjunto de valores.
(SETOF tabela/tipo);
- Todos os comandos devem ser separados por ;
- A menos que a funo seja declarada para retornar VOID, o ltimo comando
dever ser um SELECT;
- Se o retorno for VOID o ltimo comando no poder ser um SELECT;
- Qualquer comando poder ser utilizado (INSERT, SELECT, UPDATE e
DELETE).

2
Exemplos de Funes SQL:

-- para criar a funo


CREATE FUNCTION limparMEcategoriaCurso() RETURNS void AS '
DELETE FROM me_categoriaCurso;
' LANGUAGE SQL;

-- Para executar a funo


SELECT limparMEcategoriaCurso();

select * from me_categoriaCurso;

CREATE FUNCTION inserirMEcategoriaCurso() RETURNS void AS $$


insert into ME_categoriaCurso
( codCategoria,descricao,cargaHorariaMin,cargaHorariaMax )
Values
(1,'curta durao',1,20);

insert into ME_categoriaCurso


( codCategoria,descricao,cargaHorariaMin,cargaHorariaMax )
Values
(2,'Normal',21,44);

insert into ME_categoriaCurso


( codCategoria,descricao,cargaHorariaMin,cargaHorariaMax )
Values
(3,'Longa durao',45,80);

$$ LANGUAGE SQL;

select inserirMEcategoriaCurso();

select * from me_categoriaCurso;

3
CREATE FUNCTION atualizarMEcategoriaCurso(integer, integer, integer) returns
integer AS
$$

UPDATE me_categoriaCurso
set
cargaHorariamin = $2
, cargaHorariamax = $3
where
codCategoria = $1;

select 1;

$$
LANGUAGE SQL;

select atualizarMEcategoriaCurso(3, 45,90);

select * from me_categoriaCurso;

select * from me_historicoPrecoCurso ;

CREATE OR REPLACE FUNCTION


atualizarMEhistoricoPrecoCurso(decimal(5,2)) returns setof me_historicoPrecoCurso
AS
$$

UPDATE me_historicoPrecoCurso
set
valorMax = valorMax * (1.00 + $1/100.00)
where
dataFim is null;

select * from me_historicoPrecoCurso ;

$$
LANGUAGE SQL;

select * from atualizarMEhistoricoPrecoCurso(10.00);

4
Procedural Language Functions PLpgSQL

Mais flexvel que a linguagem SQL permite a utilizao de variveis e estruturas de


controle.

Criando uma funo

CREATE [or REPLACE] FUNCTION nome_funcao(par_1 tipo, par_2 tipo ... par_n)
RETURNS [SETOF] [VOID || DATATYPE || ESCALAR] AS
$$
DECLARE
BEGIN
RETURN;
END;
$$ LANGUAGE 'PLpgSQL';

Executando uma funo

SELECT nome_funcao(parametros);
ou
SELECT * FROM nome_Funcao(parmetros);

Excluindo uma funo


DROP FUNCTION nome_funcao(parmetros);

5
Exemplo de Funo

CREATE or REPLACE FUNCTION fn_exemplo_a() RETURNS integer AS $$


<<bloco_externo>> -- bloco nomeado
DECLARE
_qtd integer := 30;
BEGIN
RAISE NOTICE 'Quantidade aqui vale: %', _qtd; -- 30
_qtd := 50;
--***************
-- subbloco
--***************
DECLARE
qtd integer := 80;
BEGIN
RAISE NOTICE 'Quantidade (interno) aqui vale: %', _qtd; -- 80
RAISE NOTICE 'Quantidade (externo) aqui vale: %', bloco_externo._qtd; -- 50
END;

RAISE NOTICE 'Quantidade (externo) aqui vale: %', _qtd; -- 50

RETURN _qtd;
END;
$$ LANGUAGE plpgsql;

select fn_exemplo_a();

6
Declarao de variveis

Abaixo alguns exemplos para declarao de variveis em PostGreSQL

...
DECLARE
a integer;
b numeric(5);
c varchar(50;

linha tabela%ROWTYPE; -- declara varivel que ter a mesma estrutura de uma linha
inteira da tabela

campo tabela.coluna%TYPE; -- declara varivel de mesmo tipo da coluna da tabela


rec RECORD; -- declara varivel de tipo registro, muito semelhante a varivel linha, porm
sem tipos definidos

d integer NOT NULL DEFAULT:=80; -- varivel inteira que no pode ser nula, recebe por
padro o valor 80

e CONSTANT integer := 10; -- constante inteira inicializada com valor 10;

...

Passagem de parmetros

Como qualquer outra linguagem de programao podemos passar parmetros para um


procedimento.

Exemplo:

Create or Replace function fn_exemplo_parametros(in_a int, in_b int) returns decimal(8,2)


AS $$
DECLARE
_result decimal(8,2);
BEGIN
_result := in_a / in_b;
return _result;
END;
$$ language 'PlpgSQL';

select fn_exemplo_parametros(100,3)

Por qu nunca h nmero com casas decimais?

7
create or replace function fn_exemploTipos(p_codaluno integer) returns varchar(50) as
$$
declare
_aluno me_aluno%rowtype; -- registro do tipo aluno (possui todos as colunas da
tabela me_aluno e ir armazenar uma linha
_matriculaalunoIndicante me_aluno.matriculaAluno%type;
_msg varchar(80);
begin

select * into _aluno from me_aluno where matriculaAluno = p_codaluno;

raise notice '%', 'Atribui um valor a varivel do tipo type';


_matriculaalunoIndicante := 1;

if _matriculaalunoIndicante = _aluno.matriculaAlunoIndicante then


raise notice '%', 'Se aluno indicante igual a 10 listar o nome do aluno 10';
_msg := 'Aluno indicante : ';
select * into _aluno from me_aluno where matriculaAluno =
_matriculaalunoIndicante;
end if;

return _msg || _aluno.nome;


end;
$$ language 'plpgsql';
-- executar exemplo
select * from fn_exemploTipos(11);

Qual o problema com a funo acima?

select * from me_aluno;

Condicionais

IF ... THEN

IF ... THEN ... ELSE

IF ... THEN ... ELSE IF

IF ... THEN ... ELSIF ... THEN ... ELSE

IF ... THEN ... ELSEIF ... THEN ... ELSE

*** fim da parte I ***

8
Exerccios

a) Conforme as tabelas abaixo escreva um procedimento que atenda ao solicitado:

- O procedimento deve receber dois valores por parmetro


CNH do motorista
velocidadeApurada do veculo por ele conduzido

- O procedimento deve retornar um texto com a seguinte mensagem:


'O motorista [nome] soma [X] pontos em em multas ';

- O clculo da pontuao do motorista efetuado da seguinte forma:

Se a velocidade estiver entre 80.01 e 110 ento o motorista deve ser multado em 120,00 e
receber 20 pontos
Se a velocidade estiver entre 110.01 e 140 ento o motorista deve ser multado em 350 e
receber 40 pontos
Se a velocidade estiver eacima de 140 ento o motorista deve ser multado em 680 e receber
60 pontos

O sistema deve considerar somente 90% da velocidade apurada para o clculo da multa.

Aps o clculo o sistema deve incluir a multa na tabela ex_multa (se o contribuinte foi
multado)

Ento retornar o total acumulado de multas para o motorista.

-- criao de tabelas
create table ex_motorista
(cnh char(5) primary key
, nome varchar(20) not null
, totalMultas decimal(9,2)
);

create table ex_multa


(id serial primary key
, cnh char(5) references ex_motorista(cnh) not null
, velocidadeApurada decimal(5,2) not null
, velocidadeCalculada decimal(5,2)
, pontos integer not null
, valor decimal(9,2) not null);

-- criao de um motorista para teste


insert into ex_motorista values ('123AB', 'Carlo');

9
b) Escreva um outro procedimento que atualize o campo totalMultas da tabela ex_motorista
a partir dos totais apurados para cada motorista autuado na tabela ex_multa.
Ateno:
- motorista sem multa devero possuir valor 0.00 no campo total multa;
- cuidado para no duplicar valores na coluna totalMultas para os casos em
que a rotina for disparada mais de uma vez.

10
PlPgSql Descartando resultados de uma consulta

Motivo: Durante a execuo de um procedimento o postgreSQL tentar retornar qualquer


comando SELECT executado, exceto em duas situaes:
a) SELECT com destino, ou seja, quando o resultado do SELEC armazenado em
variveis. Exemplo SELECT campo1 INTO var1.
b) SELECT que descarte os resultados retornados. Exemplo: PERFORM

Exemplo:
-- EXEMPLO QUE DEMONSTRA A UTILIZAO DE PERFORM E FOUND

CREATE OR REPLACE FUNCTION FN_DESCARTAR_RESULT( ) RETURNS VOID


AS
$$
BEGIN

-- EXECUTA O SELECT MAS DESCARTA O RESULTADO


PERFORM * FROM EX_MULTA;

-- VARIVEL QUE LIGADA SE O COMANDO ANTERIOR (SELECT, UPDATE,


INSERT, DELETE) AFETOU AO MENOS UMA LINHA
IF FOUND THEN

RAISE NOTICE 'H MULTAS CADASTRADAS NO SISTEMA';

END IF;

RETURN;

END;
$$
LANGUAGE PLPGSQL;

SELECT FN_DESCARTAR_RESULT( );

11
PlPgSql Tratamento de Erros

O postgreSQL permite o tratamento estruturado de erros. Lembre-se que utilizar blocos


para tratamento de erros deixa o seu cdigo mais pesado.

--EXEMPLO QUE DEMONSTRA COMO TRATAR ERROS USANDO PLPGSQL

CREATE OR REPLACE FUNCTION FN_TRATAR_ERRO(_CNH CHAR(5))


RETURNS VOID AS
$$
<<BLOCO_A>>
DECLARE
_MULTA RECORD;
_MOTORISTA RECORD;
BEGIN

SELECT * INTO _MOTORISTA FROM EX_MOTORISTA WHERE cnh = _CNH;


-- FORMA I
IF NOT FOUND THEN
RAISE EXCEPTION 'MOTORISTA % NO ENCONTRADO', _CNH;
END IF;
-- FORMA II
BEGIN

SELECT * FROM EX_MULTA WHERE CNH = _CNH;

EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE NOTICE 'MOTORISTA % NO ENCONTRADO EM
MULTAS', BLOCO_A._MOTORISTA.NOME;
WHEN TOO_MANY_ROWS THEN
RAISE EXCEPTION 'MOTORISTA % POSSUI VARIAS
MULTAS', BLOCO_A._MOTORISTA.NOME;
WHEN OTHERS THEN
RAISE NOTICE '%', SQLSTATE ;
RAISE NOTICE '%', SQLERRM;

END;

RETURN;

END;
$$ LANGUAGE PLPGSQL;

12
-- INSERINDO UM MOTORISTA SEM MULTAS
INSERT INTO EX_MOTORISTA VALUES ('369EF', 'ANA');

-- EXECUTANDO OS PROCEDIMENTOS
-- TESTE I : MOTORISTA NO ENCONTRADO
SELECT FN_TRATAR_ERRO('369EC');

-- TESTE II : MOTORISTA SEM MULTAS


SELECT FN_TRATAR_ERRO('369EF');

13
PlPgSql Chamando uma funo a partir de outra

Podemos facilmente quebrar nossos procedimentos em procedimentos menores, seja para


melhorar a estrutura (manuteno) ou compreenso do mesmo.

-- CHAMANDO UM PROCEDIMENTO A PARTIR DE OUTRO


-- PROCEDIMENTO I
CREATE OR REPLACE FUNCTION FN_OBTER_TOTAL_MULTA(_CNH CHAR(5))
RETURNS DECIMAL(5,2) AS
$$
DECLARE
_TOTAL DECIMAL(5,2) :=0;
BEGIN

SELECT
COALESCE(SUM(VALOR)) AS VALOR
INTO
_TOTAL
FROM
EX_MULTA
WHERE
CNH = _CNH;

RETURN _TOTAL;

END;
$$ LANGUAGE PLPGSQL

14
-- PROCEDIMENTO Ii
CREATE OR REPLACE FUNCTION FN_ATUALIZAR_TOTAL_MULTA(_CNH
CHAR(5)) RETURNS VOID AS
$$
BEGIN

IF _CNH IS NULL THEN


RAISE EXCEPTION 'CNH NO PODE SER NULO';
END IF;

IF (SELECT FN_OBTER_TOTAL_MULTA(_CNH)) > 0 THEN


UPDATE EX_MOTORISTA
SET TOTALMULTAS = (SELECT FN_OBTER_TOTAL_MULTA(_CNH))
WHERE CNH = _CNH;
END IF;

RETURN;

END;
$$
LANGUAGE PLPGSQL;

SELECT FN_ATUALIZAR_TOTAL_MULTA('123AB');

15
PlPgSql Utilizando Funes em Consultas

--EXEMPLO : UTILIZANDO FUNES EM CONSULTAS

CREATE OR REPLACE FUNCTION FN_TEXTO_VALOR_MULTA (_VALOR


DECIMAL(8,2)) RETURNS VARCHAR(30) AS
$$
DECLARE
_MSG VARCHAR(30);
BEGIN
_MSG := 'DEVE 1000 EM MULTAS';

IF _VALOR < 1000.00 THEN


_MSG := 'MENOS DE 1000 EM MULTAS';
ELSIF _VALOR > 1000.00 THEN
_MSG := 'MAIS DE 1000 EM MULTAS';
ELSIF _VALOR IS NULL THEN
_MSG:= 'SEM MULTAS';
END IF;

-- SOMENTE A PARTIR DA VERSO 8.4


/*CASE
WHEN (_VALOR < 1000) THEN _MSG := 'MENOS DE 1000 EM
MULTAS'
WHEN (_VALOR > 1000.00) THEN _MSG := 'MAIS DE 1000 EM
MULTAS'
WHEN (_VALOR IS NULL) THEN _MSG := 'SEM MULTAS'
END;*/

RETURN _MSG;
END;
$$ LANGUAGE PLPGSQL;

SELECT
NOME
, FN_TEXTO_VALOR_MULTA(TOTALMULTAS)
, TOTALMULTAS
FROM
EX_MOTORISTA;

16
PlPgSql Utilizando Cursores e Tabelas Temporrias

Algumas vezes necessrio armazenar os dados temporariamente para a execuo de um


procedimento. Um exemplo dessa necessidade a obteno de uma pequena parte da tabela
para processamento. Deixando a tabela livre para outros acessos. Para isso usamos tabelas
temporrias.
Em outras ocasies desejamos processar um resultSet linha a linha. Nessas ocasies
utilizamos cursores. Ateno: cursores consomem muitos recursos do servidor de banco de
dados, portanto, devem ser utilizados com parcimnia.

-- EXEMPLO COM TABELAS TEMPORRIAS

CREATE OR REPLACE FUNCTION FN_EXEMPLO_TEMP_TABLE( ) RETURNS


VARCHAR(10) AS
$$
BEGIN
-- CRIAO DE TABELA TEMPORRIA. EXISTE APENAS DURANTE A
CONEXO DO USURIO
CREATE TEMP TABLE TMP_MATRICULA (CODTURMA VARCHAR(10),
MATRICULAALUNO INTEGER, CODVENDEDOR INTEGER, DATAEFETIVACAO
DATE, VALOR DECIMAL(9,2));

INSERT INTO TMP_MATRICULA


SELECT
CODTURMA, MATRICULAALUNO, CODVENDEDOR,
DATAEFETIVACAO, VALOR
FROM
ME_MATRICULA
WHERE
CODVENDEDOR = 1;

--FAZ ALGO AQUI A PARTIR DAS MATRCULAS DO VENDEDOR 1

RETURN 'FIM';

END;
$$
LANGUAGE PLPGSQL;

SELECT FN_EXEMPLO_TEMP_TABLE( ) ;
-- TENTE EXECUTAR NOVAMENTE ...
SELECT FN_EXEMPLO_TEMP_TABLE( ) ;

-- A TMP SEGUE EXISTINDO AT VOC DROPAR OU SE DESCONECTAR...

17
-- EXEMPLO COM USO DE CURSORES

CREATE OR REPLACE FUNCTION FN_ATUALIZAR_TOTAL_MULTA( )


RETURNS VOID AS
$$
DECLARE
_CURSOR_MOTORISTA REFCURSOR;
_CNH VARCHAR(5);
BEGIN

OPEN _CURSOR_MOTORISTA FOR


SELECT CNH FROM EX_MOTORISTA;

FETCH _CURSOR_MOTORISTA INTO _CNH;

WHILE FOUND LOOP

UPDATE EX_MOTORISTA
SET TOTALMULTAS = (SELECT FN_OBTER_TOTAL_MULTA(_CNH))
WHERE CNH = _CNH;

FETCH _CURSOR_MOTORISTA INTO _CNH;


END LOOP;

CLOSE _CURSOR_MOTORISTA;

RETURN;

END;
$$
LANGUAGE PLPGSQL;

SELECT FN_ATUALIZAR_TOTAL_MULTA( );

18
PlPgSql Utilizando Vetores

-- EXEMPLO DE VETORES E NMEROS ALEATRIOS

create or replace function fn_exemploArray() returns void as


$$
declare
_nome char(10) [5] := '{"Ana", "Antnia", "Amilton", "Antnio", "Armando"}'; --
criando um vetor e o populando ao mesmo tempo
_cpf char(11) [5];
_cont integer := 0; -- se no for inicializado ser inicializado com null
_aluno record;
begin

_cpf[1] := '123456';
_cpf[2] := '789456';
_cpf[3] := '654987';
_cpf[4] := '321654';
_cpf[5] := '852741';

raise notice '%', _nome[0];


raise notice '%', _cpf[0];

for i in 1..5 loop


raise notice '%', 'nome: ' || _nome[i] || ' cpf: ' || _cpf[i];
end loop;

while _cont < 5 LOOP


_cont := _cont + 1;
select * into _aluno from me_aluno order by random() limit 1;
raise notice '%', _aluno.nome;
end LOOP;

_cont := ceil(random() *5);

raise notice '%', 'nome: ' || _nome[_cont] || ' cpf: ' || _cpf[_cont];

return;

end;
$$ language 'plpgsql';

select fn_exemploArray();

19
PlPgSql Utilizando SQL Dinmico

-- EXEMPLO SQL DINMICO


CREATE OR REPLACE FUNCTION FN_SQLDINAMICO(_OP CHAR(1), _CAMPO
VARCHAR(50), _VALOR DECIMAL(9,2), _CHAVE VARCHAR(10), _VLRCHAVE
VARCHAR(5) ) RETURNS INTEGER AS $$
DECLARE
_SQL TEXT;
_TOTAL INTEGER :=0;
BEGIN
IF _OP = 'U' THEN

_SQL := 'UPDATE EX_MULTA SET ' || _CAMPO || ' = ' || _VALOR || '
WHERE ' || _CHAVE || ' = ' || QUOTE_LITERAL(_VLRCHAVE);
RAISE NOTICE '%', _SQL;

EXECUTE _SQL;

GET DIAGNOSTICS _TOTAL = ROW_COUNT; -- QUANTIDADE DE


LINHAS AFETADAS PELO LTIMO COMANDO

END IF;

RETURN _TOTAL;

END;
$$ LANGUAGE PLPGSQL;

SELECT FN_SQLDINAMICO('U', 'PONTOS', '20', 'ID', '5');

-- OBSERVEM A ABA MENSAGENS !

20
PlPgSql Retornando Conjuntos de Consultas

Algumas vezes necessrio retornar uma lista de valores de uma funo. No postgresql
temos que utilizar algumas estratgias para retornar essas listas (tabelas).

-- EXEMPLO DE RETORNO DE RESULTSETS

create or replace function fn_exemploRetornoResultSet() returns setof varchar(30) as


$$
declare
_nome varchar(30);
_aluno record;
begin

FOR _aluno IN select * from me_aluno LOOP


return next _aluno.nome;
END LOOP;

return;

end;
$$ language 'plpgsql';

select * from fn_exemploRetornoResultSet();

21
Create or replace function fn_lista_turmaVlrMedio(PAR_VLR_MIN DECIMAL(5,2) )
returns setof turma_vlr_medio as
$$
DECLARE
_turmaVlrMedio turma_vlr_medio%rowtype;
BEGIN

FOR _turmaVlrMedio IN
SELECT
codTurma, avg(valor)
FROM
me_matricula
GROUP BY
codTurma
HAVING
avg(valor) > PAR_VLR_MIN LOOP
RETURN NEXT _turmaVlrMedio;
END LOOP;
RETURN;
END;
$$ language 'plpgsql';

select * from fn_lista_turmaVlrMedio(550); -- retornando uma turma

22
EXEMPLO MAIS SIMPLES

CREATE TYPE UDT_RET_FN_LISTA_VENDEDOR AS


( C1 VARCHAR(30), C2 DECIMAL(9,2))

CREATE OR REPLACE FUNCTION


FN_LISTA_VENDEDOR(_PAR_CODVENDEDOR INT) RETURNS SETOF
UDT_RET_FN_LISTA_VENDEDOR AS $$
BEGIN

RETURN QUERY SELECT NOME, VALOR FROM ME_VENDEDOR INNER


JOIN ME_MATRICULA USING (CODVENDEDOR) WHERE CODVENDEDOR =
_PAR_CODVENDEDOR;

END;
$$ LANGUAGE PLPGSQL

SELECT * FROM FN_LISTA_VENDEDOR(1);

23
Exerccios:

a) Escreva uma funo que receba um nmero inteiro entre 1 e 7 e retorne o nome do dia da
semana baseado nesse nmero.
1 = domingo
...
7 = sbado

O procedimento deve retornar um erro caso seja passado por parmetro um nmero que no
esteja entre 1 e 7;

b) Com base na tabela ME_MATRICULA realize as seguintes tarefas:

- Crie uma tabela chamada me_resumo com os seguintes campos id serial primary key,
ano_mes integer not null, valor decimal(9,2) not null.
- Crie uma funo chamada fn_montaTabelaResumo
Passar para a funo um nmero inteiro por parmetro que corresponde a um ano
ex:. 2009, 2008, 2007 etc...
Preencher a tabela me_resumo com os valores totais recebidos por ms no ano
recebido por parmetro.
Exemplo: ano recebido 2008
na tabela me_resumo
id = 1
ano_mes = 200801
valor = 0.00

id = 2
ano_mes = 200802
valor = 500.00

etc..

A funo deve impedir a insero de valores duplicados (ano_mes) na tabela.


Meses sem matrcula em determinado ano devem receber valor igual a zero.

- Ao final a rotina deve devolver a listagem dos valores (ano_mes e valor) obtidos para a
tabela me_resumo a partir do ano passado por parmetro.

Execute a rotina vrias vezes para o mesmo ano e para anos diferentes para ter
certeza de que est funcionando.

24
c) Escreva um procedimento que retorne o nome, em letras minsculas, dos alunos
matriculados em turmas que iniciaram em meses cujo valor total de matrcula no ms
(apurado na tabela me_resumo) foram superiores a 3000.00;

d) Escreva um procedimento que permita efetuar a consulta de alunos por um dos seguintes
critrios:

- iniciais do nome
OU
- por iniciais do email
OU
- por matrcula
OU
- por CPF

Lembre-se, a rotina deve funcionar tanto para os casos em que apenas UM parmetro for
passado ou quando dois, trs ou todos forem passados.

25

Das könnte Ihnen auch gefallen