BD II (SI 587)
Procedimentos Armazenados
Josenildo Silva
jcsilva@[Link]
MOTIVAÇÃO
Contexto: Sistemas em 2 camadas
Cliente Cliente Cliente
SGBD
Contexto: Sistemas em 3 camadas
Cliente Cliente Cliente
Aplicação
SGBD
Problema 1: Alto volume de dados na rede
Como reduzir o tráfego na rede?
Cliente
Aplicação
Exemplo 1
Select SUM(qtd) as total from
produto group by codProd;
...
Para cada produto
se total < 10% do estoque SGBD
então
insert into OrdemCompra ...
Problema 1: Alto volume de dados na rede
Como reduzir o tráfego na rede?
Cliente
Aplicação
Exemplo 2
Realizar a sequência
Insert into VENDA ...
Insert into NOTAFISCAL ...
Update ESTOQUE ...
para cada venda. SGBD
Problema 1: Alto volume de dados na rede
Como reduzir o tráfego na rede?
Cliente
Aplicação
Exemplo 3
Realizar a sequência
Insert into PESSOA ...
Insert into ALUNO ...
para cada entidade em uma
hierarquia de SGBD
generalização/especialização.
Problema 1: Alto volume de dados na rede
Cliente
Aplicação
Entretanto ...
O gargalo pode ser provocado pelo
próprio servidor de banco de dados.
Caberá ao desenvolvedor, em
SGBD
conjunto com o DBA, identificar
cada caso.
Problema 2: Manutenção
Como garantir consistência de regras de negócio
nas diversas aplicações e módulos clientes?
Cliente (Java) Cliente (PHP) Cliente (Ruby)
Aplicação
SGBD
Ou seja
Algumas situações reais em grandes sistemas
Comandos repetitivos com lógica complexa
Várias linguagens de programação envolvidas
Rotinas periódicas
Todas estas situações podem se beneficiar com o uso de
procedimentos armazenados.
PROCEDIMENTOS ARMAZENADOS
Procedimentos Armazenados
São funções (rotinas) definidas no banco de dados,
identificadas por um nome pelo qual podem ser invocadas.
Um procedimento pode executar várias instruções,
receber parâmetros e retornar valores
Quando utilizar?
Quando aplicações em diferentes linguagens, ou
plataformas diferentes, executando a mesma função.
Quando utilizar?
Exemplo: Os bancos (Itaú, Bradesco, etc), em geral,
utilizam procedures para todas as operações em comum.
Por que utilizar?
Otimização: redução de tráfego de dados entre aplicação
e o SGBD
Segurança: utilizando procedures o acesso às tabelas do
banco de dados acontece de forma indireta
Manutenção: reuso do mesmo código em diversas
aplicações
Desvantagens
Necessidade de maior conhecimento da sintaxe do banco
de dados para escrita de rotinas em SQL
A sintaxe muda de um banco para outro
As rotinas ficam mais facilmente acessíveis
Alguém que tenha acesso ao banco poderá visualizar e alterar o código.
SQL/PSM
Procedimentos armazenados SQL/PSM
Procedimentos armazenados
Módulos de programa armazenados pelo SGBD no servidor de banco de
dados
Podem ser funções ou procedimentos
SQL/PSM (Persistent Stored Modules)
Extensões à SQL (publicado em 1996)
Inclui construções de programação de uso geral em SQL
Sintaxe para Procedimentos
CREATE PROCEDURE <nome> (<params>)
<declarações de variáveis locais>
<corpo do procedimento> ;
Sintaxe para Funções
CREATE FUNCTION <nome> (<params>)
RETURNS <tipo de retorno>
<declarações de variáveis locais>
<corpo da função> ;
Parâmetros
Cada parâmetro deve ter:
Tipo: um dos tipos de dados da SQL
Modo: IN, OUT ou INOUT
Parâmetros IN são o padrão, pode-se omiti-lo após o
nome
Chamando procedimentos e funções
No Mysql
CALL <nome do proc ou func>(<args>) ;
No Oracle
BEGIN
<nome do proc ou func>(<args>) ;
END
Comando de Controle (IF)
IF <condicao> THEN <lista de instrucoes>
ELSEIF <condicao> THEN <lista de instrucoes>
...
ELSEIF <condição> THEN <lista de instrucoes>
ELSE <lista de instrucoes>
END IF ;
Comando de Controle Laço (WHILE)
Comando de Controle Laço (FOR)
Exemplo
IFMA Monte Castelo
Depto. de Computação
Sistemas de Informação
Prof. Josenildo Silva
jcsilva@[Link]
@silvajc
SQL/PSM NO MYSQL 5.0
Cenário 1 (cont.)
Sistemas de venda sem SQL/PSM
O cliente faz um pedido, no qual são inseridos itens.
O pedido (bem como os itens) permanece com status
“PENDENTE” até ser confirmado.
Cenário 1
Até o pedido ser confirmado, nenhum lançamento é feito
no livro caixa
Após a confirmação fazer as ações:
Atualizar o status do pedido;
Atualizar o status dos itens do pedido;
Lançar o valor do pedido no caixa.
Esta regra de negócio tem que ser replicada em todos os
front-end (web, desktop, etc)
Cenário 1
Cenário 2
Sistemas de venda com SQL/PSM
As ações de update/insert/delete são
agrupadas em uma procedure no servidor
Sintaxe de procedures no mysql
DELIMITER $$
CREATE PROCEDURE nome_proc (parâmetros)
BEGIN
/*CORPO DO PROCEDIMENTO*/
END $$
DELIMITER ;
Delimitador é preciso inicialmente alterar o
delimitador padrão do MySQL (por exemplo $$) e
ao fim da criação do procedimento, restaurar seu
valor padrão
Exemplo 1: usando parâmetro de entrada
DELIMITER $$
CREATE PROCEDURE selecionar_prod(IN quantidade INT)
BEGIN
SELECT * FROM PRODUTOS
LIMIT quantidade;
END $$
DELIMITER ;
Para chamar este procedimento
CALL selecionar_prod(2);
Exemplo 2: usando parâmetro de saída
Armazena o total de
produtos
DELIMITER $$
CREATE PROCEDURE Quantidade_Produtos(OUT quantidade INT)
BEGIN
SELECT COUNT(*) INTO quantidade FROM PRODUTOS;
END
$$
DELIMITER ;
CALL Quantidade_Produtos(@total);
SELECT @total;
a variável poderá ser usada
posteriormente
Exemplo 3: parâmetro de entrada e saída
DELIMITER $$
CREATE PROCEDURE metade(INOUT numero INT)
BEGIN
SET numero = numero / 2;
END $$
DELIMITER ;
SET @valor = 10;
CALL metade(@valor);
SELECT @valor;
a variável poderá ser usada
posteriormente
Variáveis no corpo do procedimento
DECLARE nome_var TIPO [DEFAULT valor];
Exemplos
DECLARE quantidade int DEFAULT 0;
DECLARE valor decimal(9,2);
DECLARE total_valor decimal(9,2);
DECLARE done INT DEFAULT FALSE;
Cursores no MySQL
Itera um conjunto de tuplas
Utiliza-se dentro de uma procedure, function ou triggers
Sintaxe
DECLARE nome FOR (SELECT ...)
Usando Cursores no MySQL
OPEN nome
para abrir o cursor , que fará com que ele executea consulta;
Logo em seguida inicia o loop com a seguinte instrução
– nome_cursor : loop
FETCH nome INTO
para atribuir o retorno do cursor a uma ou mais variáveis;
CLOSE nome
Exemplo de Cursor no MySQL
CREATE PROCEDURE cursordemo()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE a CHAR(16);
DECLARE b, c INT;
DECLARE cur1 CURSOR FOR SELECT id,data FROM t1;
DECLARE cur2 CURSOR FOR SELECT i FROM t2;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
OPEN cur2;
read_loop: LOOP
FETCH cur1 INTO a, b;
FETCH cur2 INTO c;
IF done THEN
LEAVE read_loop;
END IF;
IF b < c THEN
INSERT INTO test.t3 VALUES (a,b);
ELSE
INSERT INTO test.t3 VALUES (a,c);
END IF;
END LOOP;
CLOSE cur1;
CLOSE cur2;
END;
Fonte: [Link]
Cenário: controle de estoque
O vendedor deve verificar a disponibilidade de um
determinado produto em tempo real.
O setor de compras deve planejar melhor as atividades de
compra.
Cenário: controle de estoque
Não é recomendado inserir, atualizar e somente recebe os dados
excluir diretamente nesta tabela conforme as
ações executadas nas
tabelas de
ENTRADA e SAIDA de
Produto.
Registra as compras Registra as vendas
Procedure que atualiza o estoque
delimiter $$
create procedure pAtualizaEstoque(idProd int,
qtdeComprada int,
valorUnitario decimal(9,2) )
begin
declare existeProd int;
select id_produto into existeProd from estoque
where id_produto = idProd;
if (existeProd is not null) then
update estoque set qtde = qtde + qtdeComprada,
valor_unitario = valorUnitario
where id_produto = idProd;
else
insert into estoque (id_produto, qtde, valor_unitario)
values (idProd, qtdeComprada,valorUnitario);
end if;
end $$
delimiter ;
SQL/PSM NO ORACLE
Procedure no Oracle
DECLARE
// Coloque as variáveis aqui
BEGIN
// Aqui vai o código
EXCEPTION
// Tratamento de erros
END;
Procedure no Oracle
declare
media INT := 10;
begin
select avg(SALARY) into media from EMPLOYEES;
DBMS_OUTPUT.PUT_LINE(‘Media:’|| TO_CHAR(media) );
end;
Procedure no Oracle
CREATE [OR REPLACE] PROCEDURE <nome>
[ (par1 type1, par2 type2,...) ]
IS | AS
DECLARE
....
BEGIN
...
EXCEPTION
...
END <name>;
Exemplo de Procedure no Oracle
CREATE OR REPLACE PROCEDURE PDate IS
BEGIN
DBMS_OUTPUT.PUT_LINE('DATE:'||TO_CHAR(SYSDATE));
END PDate;
Para fazer a chamada (execução) crie um bloco simples:
BEGIN
PDate();
END;
Exemplo de Procedure com Parametro IN
CREATE OR REPLACE PROCEDURE PROC_ID(p_id
EMPLOYEES.EMPLOYEE_ID%TYPE) IS
BEGIN
UPDATE EMPLOYEES
SET LAST_NAME = LAST_NAME || ' X'
WHERE EMPLOYEE_ID = p_id;
COMMIT;
END PROC_ID;
Exemplo de Procedure com Parametro IN
-- Execução com parâmetros posicionais
BEGIN
PROC_ID(176);
END;
-- Execução com parâmetros nomeados
BEGIN
PROC_ID(p_id=>176);
END;
Efeito
SELECT * FROM EMPLOYEES
WHERE EMPLOYEE_ID = 176;
Exemplo de Procedure com parametro OUT
CREATE OR REPLACE PROCEDURE PROC_NAME(
p_id IN EMPLOYEES.EMPLOYEE_ID%TYPE,
p_name OUT EMPLOYEES.LAST_NAME%TYPE )
IS
BEGIN
SELECT LAST_NAME INTO p_name
FROM EMPLOYEES
WHERE EMPLOYEE_ID = p_id;
END PROC_NAME;
Exemplo de Procedure com parametro OUT
-- Execução
DECLARE
v_name varchar(100);
BEGIN
PROC_NAME(176,v_name);
DBMS_OUTPUT.PUT_LINE(Nome:'||TO_CHAR(v_name));
END;
Exemplo com parametros in out no Oracle
CREATE OR REPLACE PROCEDURE PROC_CONCAT(P_CHAR IN
OUT VARCHAR2 )
IS
BEGIN
P_CHAR:= '(' || P_CHAR || ')';
END PROC_CONCAT;
Procedure no Oracle
-- Execução
DECLARE
ret VARCHAR(100);
BEGIN
FOR emp_rec IN (SELECT LAST_NAME FROM EMPLOYEES )
LOOP
DBMS_OUTPUT.PUT_LINE(Nome: ' ||
TO_CHAR(emp_rec.LAST_NAME) );
PROC_CONCAT(emp_rec.LAST_NAME);
DBMS_OUTPUT.PUT_LINE(‘Nome: ' ||
TO_CHAR(emp_rec.LAST_NAME) );
END LOOP;
END;
Exemplo de parametro IN OUT
create or replace
procedure pAtualizaEstoque(
idProd [Link]%TYPE,
qtdeComprada [Link]%TYPE,
valorUnitario ESTOQUE.VALOR_UNITARIO%TYPE)
cont...
is
total number;
begin
select count(id_produto) into total
from estoque where id_produto = idProd;
if (total > 0) then
DBMS_OUTPUT.PUT_LINE('Antes de atualizar ....');
update estoque
set qtde = qtde + qtdeComprada,
valor_unitario = valorUnitario
where id_produto = idProd;
else
DBMS_OUTPUT.PUT_LINE('Antes de inserir ....');
insert into estoque (id, id_produto,
qtde, valor_unitario)
values (seq_idEstoque.nextval, idProd,
qtdeComprada, valorUnitario);
end if;
end pAtualizaEstoque;
Parametros default
create or replace procedure proc_default(x int
default 1)
is
begin
dbms_output.put_line('value: ' ||
to_char(x*1) );
end proc_default;
Parametros default
-- Execução
BEGIN
PROC_DEFAULT();
PROC_DEFAULT(3);
END;
Leitura Adicional
Seção 9.6 do SILBERSCHATZ: Funções e Procedimentos
Seção 13.4 do NAVATHE: Procedimentos armazendados
Tutorial sobre Procedimentos no MySQL por Wagner Bianchi.
[Link]
_mysql/
[Link]
[Link]
Links interessantes
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]