Introdução
Quando você manda uma query SQL avulsa, o banco faz isso toda vez:
- Parse — analisa a sintaxe.
- Otimização — decide o melhor plano de execução (quais índices usar, ordem dos JOINs, etc.).
- Execução — roda de fato.
Uma stored procedure/function é salva no banco com nome próprio, e o banco guarda (ou reutiliza) o plano de execução já otimizado para as próximas chamadas. Isso poupa o trabalho de reanalisar e replanejar a mesma lógica repetidamente — é isso que "precompilado" quer dizer na prática (não é bytecode compilado feito um .exe, é o plano de execução em cache).
Stored Procedures
Um bloco de código SQL + lógica procedural (variáveis, IF, WHILE, loops) salvo no banco, que você executa/chama — não usa dentro de um SELECT.
Sintaxe (varia bastante entre bancos)
SQL Server (T-SQL):
CREATE PROCEDURE TransferirSaldo
@conta_origem INT,
@conta_destino INT,
@valor DECIMAL(10,2)
AS
BEGIN
UPDATE contas SET saldo = saldo - @valor WHERE id = @conta_origem;
UPDATE contas SET saldo = saldo + @valor WHERE id = @conta_destino;
END;
-- Chamando:
EXEC TransferirSaldo @conta_origem= 1, @conta_destino= 2, @valor= 100;
Repare que já conectamos com assunto de Transact e ACID: a procedure encapsula a transação inteira (os dois UPDATEs) — isso é extremamente comum na prática. A lógica de negócio "transferência bancária" mora em um único lugar, versionada e reutilizável.
Parâmetros: IN, OUT e INOUT
IN (padrão) |
Recebe um valor de fora para dentro da procedure |
|---|---|
OUT |
Devolve um valor calculado de dentro para fora, para a variável do chamador |
INOUT |
Funciona nos dois sentidos — entra com um valor, sai modificado |
Lógica procedural: variáveis, IF, loops
Isso é o que diferencia uma procedure de um SELECT comum. Ela pode ter controle de fluxo, como uma linguagem de programação de verdade:
CREATE PROCEDURE processar_pedidos_pendentes()
LANGUAGE plpgsql
AS $$
DECLARE
pedido RECORD;
contadorINT := 0;
BEGIN
FOR pedidoIN SELECT * FROM pedidos WHERE status = 'pendente' LOOP
IF pedido.total > 1000 THEN
UPDATE pedidos SET status = 'em_analise' WHERE id = pedido.id;
ELSE
UPDATE pedidos SET status = 'aprovado' WHERE id = pedido.id;
END IF;
contador := contador+ 1;
END LOOP;
RAISE NOTICE'% pedidos processados', contador;
END;
$$;
Isso é impossível de fazer em um SELECT puro. É por isso que procedures existem: para lógica que precisa de passos sequenciais, decisões e repetição, não só "buscar dados".
Functions
Um bloco de código salvo no banco que obrigatoriamente retorna um valor, e pode ser usado dentro de outras queries — em SELECT, WHERE, ORDER BY — exatamente como ROUND() ou UPPER() (que são, na verdade, funções nativas do banco; as suas são "customizadas").
Sintaxe
CREATE FUNCTION CalcularIdade(@data_nascimento DATE)
RETURNS INT
AS
BEGIN
RETURN DATEDIFF(YEAR, @data_nascimento, GETDATE());
END;
SELECT nome, dbo.CalcularIdade(data_nascimento) AS idade FROM clientes;
Scalar Function vs. Table-Valued Function
| Tipo | Retorna | Exemplo de uso |
|---|---|---|
| Scalar | Um único valor (número, texto, data) |
calcular_idade() acima — um INT por linha |
| Table-valued | Uma tabela inteira (linhas e colunas) | Uma função que retorna "todos os pedidos de um cliente" |
-- Table-valued function (PostgreSQL):
CREATE FUNCTION pedidos_do_cliente(cliente_id INT)
RETURNS TABLE(pedido_id INT, total NUMERIC, data DATE)
LANGUAGE plpgsql
AS $$
BEGIN
RETURN QUERY
SELECT id, total, data_pedido FROM pedidos WHERE cliente_id = $1;
END;
$$;
-- Usando como se fosse uma tabela:
SELECT * FROM pedidos_do_cliente(1) WHERE total > 100;
Isso conecta diretamente com view. Uma table-valued function é como uma "view com parâmetros". A view não aceita argumentos; a function sim.
Diferenças cruciais: Procedure vs. Function
| Stored Procedure | Function | |
|---|---|---|
| Retorno | Opcional (pode não retornar nada, ou retornar via OUT) |
Obrigatório — sempre retorna um valor |
| Uso em SELECT | ❌ Não pode ser usada dentro de um SELECT/WHERE
|
✅ Pode ser usada como se fosse uma coluna/expressão |
| Como se chama |
CALL procedure(...) / EXEC procedure
|
Dentro de uma query: SELECT function(...)
|
| Transações | ✅ Pode conter COMMIT/ROLLBACK dentro dela |
❌ Geralmente não pode controlar transação (roda dentro da transação de quem chamou) |
| Parâmetros de saída | Suporta OUT/INOUT
|
Só retorno via RETURN (sem OUT na maioria dos bancos) |
| Efeitos colaterais (side effects) | Esperado — fazer INSERT/UPDATE/DELETE é o uso típico |
Deveria ser evitado — functions idealmente são "puras" (calculam e retornam, sem alterar dados), embora tecnicamente algumas linguagens permitam |
| Uso típico | Automatizar processos, lotes, transações complexas | Cálculos reutilizáveis, regras de negócio usadas em queries |
Regra prática para decidir qual usar: se você precisa buscar/calcular e devolver um valor para usar dentro de uma query, use function. Se você precisa executar uma sequência de ações (múltiplos UPDATEs, lógica de transação, processamento em lote), use procedure.
Top comments (0)