{"id":1464,"date":"2026-07-10T17:51:27","date_gmt":"2026-07-10T20:51:27","guid":{"rendered":"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/"},"modified":"2026-07-10T17:51:27","modified_gmt":"2026-07-10T20:51:27","slug":"aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo","status":"publish","type":"post","link":"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/","title":{"rendered":"Aula 16: Functions e Stored Procedures em PL\/pgSQL \u2014 Domine a L\u00f3gica no Banco de Dados"},"content":{"rendered":"<p>Nesta aula, vamos mergulhar de cabe\u00e7a em um dos recursos mais poderosos do PostgreSQL: a cria\u00e7\u00e3o de <strong>Functions e Stored Procedures<\/strong> utilizando a linguagem <strong>PL\/pgSQL<\/strong>. Se voc\u00ea j\u00e1 domina consultas SQL, mas ainda delega a l\u00f3gica de neg\u00f3cio exclusivamente \u00e0 camada da aplica\u00e7\u00e3o, prepare-se para uma mudan\u00e7a de paradigma. Ao final deste conte\u00fado, voc\u00ea compreender\u00e1 profundamente as diferen\u00e7as entre fun\u00e7\u00f5es e procedimentos armazenados, saber\u00e1 quando utilizar cada um e, principalmente, ser\u00e1 capaz de escrever c\u00f3digo procedural robusto, eficiente e seguro diretamente no cora\u00e7\u00e3o do banco de dados. A palavra-chave que guiar\u00e1 todo o nosso aprendizado \u00e9 justamente <strong>Functions e Stored Procedures<\/strong>, pois elas representam a espinha dorsal da programa\u00e7\u00e3o server-side no ecossistema PostgreSQL.<\/p>\n<p>Por que este tema \u00e9 t\u00e3o relevante para profissionais de infraestrutura, seguran\u00e7a da informa\u00e7\u00e3o e tecnologia? A resposta est\u00e1 na performance e na integridade. Ao mover l\u00f3gicas complexas para <strong>Functions e Stored Procedures<\/strong>, voc\u00ea reduz drasticamente o tr\u00e1fego de rede entre a aplica\u00e7\u00e3o e o servidor de dados, encapsula regras de neg\u00f3cio sens\u00edveis e imp\u00f5e uma camada extra de valida\u00e7\u00e3o e auditoria que nenhum ORM consegue oferecer isoladamente. Em nossos projetos na JRT Technology Solutions, nossos especialistas utilizam diariamente esses recursos para implementar desde valida\u00e7\u00f5es de integridade referencial customizadas at\u00e9 pipelines completos de ETL que processam milh\u00f5es de registros em janelas de manuten\u00e7\u00e3o reduzidas, sempre com performance previs\u00edvel e controlada.<\/p>\n<p>O leitor que nos acompanha desde o in\u00edcio do curso &#8220;PostgreSQL \u2014 Do Zero ao Avan\u00e7ado&#8221; j\u00e1 possui a base necess\u00e1ria em administra\u00e7\u00e3o de bancos, cria\u00e7\u00e3o de tabelas, \u00edndices, views e CTEs. Se voc\u00ea perdeu alguma aula anterior ou ainda n\u00e3o se sente seguro com esses fundamentos, recomendamos revisitar as Aulas 12 a 15, onde cobrimos consultas avan\u00e7adas e otimiza\u00e7\u00e3o. Nesta Aula 16, partiremos do princ\u00edpio de que voc\u00ea tem um cluster PostgreSQL funcional (vers\u00e3o 14 ou superior \u00e9 recomendada, pois alguns recursos avan\u00e7ados de <strong>Stored Procedures<\/strong> foram estabilizados nessas vers\u00f5es) e acesso como superusu\u00e1rio ou propriet\u00e1rio do banco de dados de testes.<\/p>\n<p>Prepare-se para uma aula densa, com mais de 4.000 palavras recheadas de exemplos pr\u00e1ticos, comandos completos comentados linha a linha e sa\u00eddas de console reais. Vamos construir fun\u00e7\u00f5es escalares, fun\u00e7\u00f5es que retornam tabelas, procedimentos com controle transacional expl\u00edcito e triggers que reagem a eventos de dados. Tudo isso seguindo um roteiro did\u00e1tico e progressivo. Ao final, voc\u00ea ter\u00e1 um arsenal de t\u00e9cnicas para resolver problemas reais do dia a dia, seja em ambientes de produ\u00e7\u00e3o cr\u00edtica ou em laborat\u00f3rios de desenvolvimento. E lembre-se: a JRT Technology Solutions oferece treinamentos, implementa\u00e7\u00e3o e suporte em PostgreSQL para empresas que desejam levar sua infraestrutura de dados ao pr\u00f3ximo n\u00edvel de maturidade.<\/p>\n<h3>O que voc\u00ea vai aprender nesta aula<\/h3>\n<p>A Aula 16 foi cuidadosamente estruturada para transformar voc\u00ea em um desenvolvedor PL\/pgSQL confiante. N\u00e3o se trata apenas de decorar sintaxe, mas de entender os fundamentos que permitem tomar decis\u00f5es arquiteturais corretas. Ao terminar esta leitura e executar todos os exemplos propostos, voc\u00ea ter\u00e1 dom\u00ednio sobre os seguintes t\u00f3picos:<\/p>\n<ul>\n<li><strong>Diferen\u00e7as fundamentais entre Functions e Stored Procedures<\/strong> \u2014 quando usar um ou outro, incluindo caracter\u00edsticas transacionais e de retorno.<\/li>\n<li><strong>Sintaxe completa de CREATE FUNCTION e CREATE PROCEDURE<\/strong> \u2014 par\u00e2metros, linguagens suportadas, volatilidade e seguran\u00e7a.<\/li>\n<li><strong>Tipos de par\u00e2metros IN, OUT e INOUT<\/strong> \u2014 como utiliz\u00e1-los para criar interfaces limpas e reutiliz\u00e1veis.<\/li>\n<li><strong>Estruturas de controle em PL\/pgSQL<\/strong> \u2014 blocos condicionais (IF, CASE), la\u00e7os de repeti\u00e7\u00e3o (LOOP, WHILE, FOR) e tratamento de exce\u00e7\u00f5es.<\/li>\n<li><strong>Fun\u00e7\u00f5es com retorno de conjuntos (SETOF e RETURNS TABLE)<\/strong> \u2014 essenciais para criar fontes de dados din\u00e2micas.<\/li>\n<li><strong>Triggers e sua integra\u00e7\u00e3o com fun\u00e7\u00f5es<\/strong> \u2014 como automatizar valida\u00e7\u00f5es e auditorias.<\/li>\n<li><strong>Boas pr\u00e1ticas de seguran\u00e7a<\/strong> \u2014 uso adequado de SECURITY DEFINER e SECURITY INVOKER para evitar escalonamento de privil\u00e9gios.<\/li>\n<li><strong>Depura\u00e7\u00e3o e logging com RAISE<\/strong> \u2014 t\u00e9cnicas para diagnosticar problemas em c\u00f3digo procedural.<\/li>\n<li><strong>Erros comuns e suas solu\u00e7\u00f5es<\/strong> \u2014 com exemplos reais de mensagens de erro e os passos exatos para corrigi-los.<\/li>\n<li><strong>Comparativo de performance<\/strong> \u2014 quando o overhead do PL\/pgSQL compensa e quando \u00e9 prefer\u00edvel SQL puro.<\/li>\n<\/ul>\n<h3>Pr\u00e9-requisitos e Ambiente<\/h3>\n<p>Antes de iniciarmos a parte pr\u00e1tica, vamos garantir que seu ambiente est\u00e1 perfeitamente configurado. Voc\u00ea precisar\u00e1 de um servidor PostgreSQL em execu\u00e7\u00e3o \u2014 pode ser uma instala\u00e7\u00e3o local, um cont\u00eainer Docker ou uma inst\u00e2ncia em nuvem. Nesta aula, utilizaremos um banco de dados chamado <strong>curso_functions<\/strong>, que criaremos juntos, e faremos todos os testes como o usu\u00e1rio <strong>postgres<\/strong> ou um usu\u00e1rio com privil\u00e9gios de cria\u00e7\u00e3o de objetos. Se voc\u00ea utiliza ambientes separados de produ\u00e7\u00e3o, adapte os comandos conforme sua pol\u00edtica de seguran\u00e7a.<\/p>\n<p>Em nossos treinamentos na JRT Technology Solutions, sempre recomendamos que os alunos trabalhem com uma c\u00f3pia de seguran\u00e7a do banco antes de testar c\u00f3digo procedural em produ\u00e7\u00e3o. Embora nesta aula estejamos em ambiente controlado, a disciplina de realizar backups antes de altera\u00e7\u00f5es estruturais \u00e9 uma pr\u00e1tica que salva carreiras. Dito isso, vamos criar o banco de dados de exemplo e uma tabela sobre a qual aplicaremos nossas primeiras fun\u00e7\u00f5es e procedimentos.<\/p>\n<p>Execute os comandos a seguir como superusu\u00e1rio do PostgreSQL. Eles criar\u00e3o o banco <strong>curso_functions<\/strong>, a tabela <strong>produtos<\/strong> e inserir\u00e3o alguns registros de exemplo. Este ser\u00e1 nosso playground para todo o restante da aula.<\/p>\n<pre><code>-- Conecte-se ao cluster como superusu\u00e1rio (exemplo via psql)\npsql -U postgres -h localhost\n\n-- Cria\u00e7\u00e3o do banco de dados dedicado para esta aula\nCREATE DATABASE curso_functions\n    WITH ENCODING 'UTF8'\n    LC_COLLATE 'pt_BR.UTF-8'\n    LC_CTYPE 'pt_BR.UTF-8'\n    TEMPLATE template0;\n\n-- Mensagem de confirma\u00e7\u00e3o (exibida no psql)\n-- CREATE DATABASE\n\n-- Conectar ao novo banco\n\\c curso_functions\n\n-- Cria\u00e7\u00e3o da tabela produtos com constraints realistas\nCREATE TABLE produtos (\n    id          SERIAL PRIMARY KEY,\n    nome        VARCHAR(150) NOT NULL,\n    categoria   VARCHAR(50) NOT NULL,\n    preco       NUMERIC(10, 2) NOT NULL CHECK (preco > 0),\n    estoque     INTEGER NOT NULL DEFAULT 0 CHECK (estoque >= 0),\n    data_criacao TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP\n);\n\n-- Cria\u00e7\u00e3o de um \u00edndice para buscas por categoria (uso frequente nas fun\u00e7\u00f5es)\nCREATE INDEX idx_produtos_categoria ON produtos (categoria);\n\n-- Inser\u00e7\u00e3o de dados de exemplo variados\nINSERT INTO produtos (nome, categoria, preco, estoque) VALUES\n    ('Teclado Mec\u00e2nico RGB', 'Perif\u00e9ricos', 349.90, 50),\n    ('Mouse Gamer 16000DPI', 'Perif\u00e9ricos', 249.90, 120),\n    ('Monitor 27\" 4K', 'Monitores', 2499.00, 15),\n    ('Notebook i7 16GB', 'Computadores', 5499.00, 8),\n    ('SSD NVMe 1TB', 'Armazenamento', 599.90, 200),\n    ('Roteador Wi-Fi 6', 'Redes', 449.90, 35),\n    ('Headset Surround', 'Perif\u00e9ricos', 399.90, 60),\n    ('Cadeira Gamer', 'M\u00f3veis', 1299.00, 10);\n\n-- Verificar o conte\u00fado inserido\nSELECT count(*) AS total_produtos FROM produtos;\n<\/code><\/pre>\n<p>A sa\u00edda esperada ap\u00f3s a inser\u00e7\u00e3o e consulta deve ser semelhante a esta:<\/p>\n<pre><code class=\"output\"> total_produtos\n----------------\n              8\n(1 row)\n<\/code><\/pre>\n<p>Agora que temos o ambiente preparado, podemos come\u00e7ar a explorar o fascinante mundo da programa\u00e7\u00e3o procedural. Observe que cada comando foi explicado: o uso de <strong>TEMPLATE template0<\/strong> garante que n\u00e3o herdaremos configura\u00e7\u00f5es de locale de outros bancos, e as constraints <strong>CHECK<\/strong> s\u00e3o fundamentais para manter a integridade, mesmo quando manipulamos dados via fun\u00e7\u00f5es.<\/p>\n<h3>Functions vs. Stored Procedures \u2014 Entendendo as Diferen\u00e7as Cruciais<\/h3>\n<p>Um dos pontos de maior confus\u00e3o para quem est\u00e1 iniciando no ecossistema PostgreSQL \u00e9 a distin\u00e7\u00e3o entre <strong>Functions<\/strong> e <strong>Stored Procedures<\/strong>. Embora em muitos SGBDs (Sistemas Gerenciadores de Banco de Dados) os termos sejam tratados como sin\u00f4nimos, o PostgreSQL estabelece diferen\u00e7as claras e pr\u00e1ticas que impactam diretamente a arquitetura da sua aplica\u00e7\u00e3o. Compreender essas diferen\u00e7as \u00e9 o primeiro passo para usar cada recurso no cen\u00e1rio adequado e evitar armadilhas transacionais.<\/p>\n<p>As <strong>Functions<\/strong> (criadas com <code>CREATE FUNCTION<\/code>) existem no PostgreSQL h\u00e1 d\u00e9cadas e s\u00e3o extremamente vers\u00e1teis. Elas obrigatoriamente retornam um valor \u2014 que pode ser um tipo escalar, um registro, uma tabela ou at\u00e9 mesmo <code>void<\/code> (que na verdade retorna <code>NULL<\/code> de forma impl\u00edcita). Uma caracter\u00edstica fundamental \u00e9 que <strong>Functions n\u00e3o podem controlar transa\u00e7\u00f5es<\/strong>: voc\u00ea n\u00e3o pode executar <code>COMMIT<\/code> ou <code>ROLLBACK<\/code> dentro de uma fun\u00e7\u00e3o. Elas sempre executam dentro do contexto transacional da chamada externa. Se uma fun\u00e7\u00e3o falha, toda a transa\u00e7\u00e3o \u00e9 abortada, a menos que voc\u00ea capture a exce\u00e7\u00e3o explicitamente com um bloco <code>EXCEPTION<\/code>.<\/p>\n<p>J\u00e1 os <strong>Stored Procedures<\/strong> (criados com <code>CREATE PROCEDURE<\/code>) foram introduzidos no PostgreSQL 11 e representam uma adi\u00e7\u00e3o mais moderna ao arsenal de programa\u00e7\u00e3o server-side. A diferen\u00e7a mais marcante \u00e9 que <strong>Procedures permitem controle transacional expl\u00edcito<\/strong>. Dentro de um procedimento, voc\u00ea pode iniciar subtransa\u00e7\u00f5es, confirmar partes do trabalho ou reverter opera\u00e7\u00f5es sem afetar necessariamente toda a chamada \u2014 embora com as devidas ressalvas de isolamento e concorr\u00eancia. Procedures <strong>n\u00e3o retornam valores<\/strong> (a n\u00e3o ser por meio de par\u00e2metros <code>INOUT<\/code>), e s\u00e3o chamadas exclusivamente via <code>CALL<\/code>, nunca como parte de uma consulta SQL.<\/p>\n<p>Para consolidar esse entendimento, observe a tabela comparativa abaixo. Em nossos projetos na JRT Technology Solutions, frequentemente optamos por <strong>Functions<\/strong> para c\u00e1lculos escalares e transforma\u00e7\u00f5es que participam de consultas, enquanto <strong>Stored Procedures<\/strong> s\u00e3o reservados para fluxos de trabalho complexos que exigem controle granular sobre o commit de dados em etapas distintas.<\/p>\n<table border=\"1\" cellpadding=\"8\" cellspacing=\"0\" style=\"border-collapse: collapse; width: 100%;\">\n<thead>\n<tr style=\"background-color: #f0f0f0;\">\n<th style=\"text-align: left; padding: 10px;\">Caracter\u00edstica<\/th>\n<th style=\"text-align: left; padding: 10px;\">Functions (CREATE FUNCTION)<\/th>\n<th style=\"text-align: left; padding: 10px;\">Stored Procedures (CREATE PROCEDURE)<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"padding: 8px;\">Retorno de valor<\/td>\n<td style=\"padding: 8px;\">Obrigat\u00f3rio (escalar, tabela, void)<\/td>\n<td style=\"padding: 8px;\">N\u00e3o retorna valor (usa INOUT se necess\u00e1rio)<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 8px;\">Controle de transa\u00e7\u00f5es<\/td>\n<td style=\"padding: 8px;\">N\u00e3o permitido (COMMIT\/ROLLBACK)<\/td>\n<td style=\"padding: 8px;\">Permitido (subtransa\u00e7\u00f5es expl\u00edcitas)<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 8px;\">Uso em consultas SQL<\/td>\n<td style=\"padding: 8px;\">Sim (SELECT, FROM, WHERE etc.)<\/td>\n<td style=\"padding: 8px;\">N\u00e3o (apenas via CALL)<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 8px;\">Linguagens suportadas<\/td>\n<td style=\"padding: 8px;\">PL\/pgSQL, SQL, C, Python, Perl etc.<\/td>\n<td style=\"padding: 8px;\">PL\/pgSQL, SQL, Python, Perl etc.<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 8px;\">Execu\u00e7\u00e3o em paralelo<\/td>\n<td style=\"padding: 8px;\">Pode ser marcada como PARALLEL SAFE<\/td>\n<td style=\"padding: 8px;\">N\u00e3o se aplica<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>Criando sua Primeira Function \u2014 Estrutura B\u00e1sica e Par\u00e2metros<\/h3>\n<p>Vamos iniciar a jornada pr\u00e1tica com a cria\u00e7\u00e3o de uma <strong>function<\/strong> simples que recebe um pre\u00e7o e retorna o valor com uma margem de lucro aplicada. Este exemplo introduzir\u00e1 a sintaxe fundamental de <strong>CREATE FUNCTION<\/strong>, os modos de par\u00e2metros (<strong>IN<\/strong>, padr\u00e3o) e o bloco <code>$$<\/code> que delimita o corpo da fun\u00e7\u00e3o. Em PL\/pgSQL, todo c\u00f3digo procedural \u00e9 encapsulado entre <code>$$ ... $$<\/code> ou entre aspas delimitadas por <code>$tag$ ... $tag$<\/code>.<\/p>\n<p>A estrutura m\u00ednima de uma fun\u00e7\u00e3o PL\/pgSQL inclui: nome, lista de par\u00e2metros com tipos, declara\u00e7\u00e3o de tipo de retorno, especifica\u00e7\u00e3o de linguagem (<code>LANGUAGE plpgsql<\/code>) e o corpo delimitado. Opcionalmente, podemos especificar volatilidade, seguran\u00e7a e comportamento de paraleliza\u00e7\u00e3o. A seguir, criamos a fun\u00e7\u00e3o <strong>calcular_preco_venda<\/strong> que aplica 30% de margem sobre o pre\u00e7o de custo fornecido.<\/p>\n<pre><code>-- Cria\u00e7\u00e3o da primeira function com um par\u00e2metro de entrada e retorno NUMERIC\nCREATE OR REPLACE FUNCTION calcular_preco_venda(\n    preco_custo NUMERIC  -- Par\u00e2metro IN impl\u00edcito (modo padr\u00e3o)\n)\nRETURNS NUMERIC  -- Tipo de dado retornado\nLANGUAGE plpgsql  -- Linguagem utilizada no corpo da fun\u00e7\u00e3o\nIMMUTABLE  -- Sempre retorna o mesmo resultado para os mesmos par\u00e2metros\n-- IMMUTABLE permite que o PostgreSQL otimize chamadas repetidas\nAS $$\nBEGIN\n    -- Corpo da fun\u00e7\u00e3o: aplica margem de 30% e arredonda para 2 casas decimais\n    RETURN ROUND(preco_custo * 1.30, 2);\nEND;\n$$;\n\n-- A mensagem de sucesso deve ser:\n-- CREATE FUNCTION\n<\/code><\/pre>\n<p>Agora, vamos testar nossa fun\u00e7\u00e3o rec\u00e9m-criada em uma consulta SQL real. A beleza das <strong>functions<\/strong> \u00e9 que elas podem ser invocadas diretamente no <code>SELECT<\/code>, como se fossem fun\u00e7\u00f5es nativas do banco. Execute o comando abaixo:<\/p>\n<pre><code>-- Teste da function calcular_preco_venda\nSELECT \n    nome,\n    preco,\n    calcular_preco_venda(preco) AS preco_sugerido,\n    calcular_preco_venda(preco) - preco AS lucro_unitario\nFROM produtos\nWHERE categoria = 'Perif\u00e9ricos'\nORDER BY preco_sugerido DESC;\n<\/code><\/pre>\n<p>A sa\u00edda esperada no seu terminal ou client SQL deve exibir tr\u00eas produtos da categoria Perif\u00e9ricos com os respectivos pre\u00e7os, pre\u00e7os sugeridos e lucro unit\u00e1rio:<\/p>\n<pre><code class=\"output\">         nome          |  preco  | preco_sugerido | lucro_unitario\n------------------------+---------+----------------+----------------\n Teclado Mec\u00e2nico RGB   |  349.90 |         454.87 |         104.97\n Headset Surround       |  399.90 |         519.87 |         119.97\n Mouse Gamer 16000DPI   |  249.90 |         324.87 |          74.97\n(3 rows)\n<\/code><\/pre>\n<p>Observe como a fun\u00e7\u00e3o foi invocada duas vezes na mesma consulta sem nenhum efeito colateral, gra\u00e7as \u00e0 propriedade <strong>IMMUTABLE<\/strong>. O PostgreSQL pode, inclusive, armazenar em cache o resultado para evitar rec\u00e1lculos desnecess\u00e1rios. No entanto, cuidado: marcar uma fun\u00e7\u00e3o como <code>IMMUTABLE<\/code> que acessa tabelas ou depende de configura\u00e7\u00f5es de sess\u00e3o causar\u00e1 resultados inconsistentes. Em projetos na JRT Technology Solutions, sempre auditamos as fun\u00e7\u00f5es antes de aplicar marcadores de volatilidade.<\/p>\n<h3>Par\u00e2metros IN, OUT e INOUT \u2014 Interfaces Flex\u00edveis e Poderosas<\/h3>\n<p>Ao definir <strong>functions e stored procedures<\/strong>, voc\u00ea n\u00e3o est\u00e1 limitado ao modo padr\u00e3o de par\u00e2metros de entrada. O PostgreSQL oferece tr\u00eas modos distintos que transformam a maneira como voc\u00ea projeta suas interfaces: <strong>IN<\/strong> (entrada, somente leitura), <strong>OUT<\/strong> (sa\u00edda, retorna valores) e <strong>INOUT<\/strong> (entrada e sa\u00edda simult\u00e2neas). Esta flexibilidade \u00e9 um diferencial importante, especialmente quando voc\u00ea precisa retornar m\u00faltiplos valores de tipos diferentes sem criar um tipo composto explicitamente.<\/p>\n<p>Par\u00e2metros <strong>OUT<\/strong> s\u00e3o particularmente elegantes porque eliminam a necessidade de uma cl\u00e1usula <code>RETURNS<\/code> separada. Quando voc\u00ea declara um ou mais par\u00e2metros como <code>OUT<\/code>, o PostgreSQL automaticamente define a estrutura de retorno com base nesses par\u00e2metros. Isso significa que uma fun\u00e7\u00e3o pode &#8220;retornar&#8221; m\u00faltiplas colunas nomeadas sem esfor\u00e7o adicional. Par\u00e2metros <strong>INOUT<\/strong>, por sua vez, funcionam como vari\u00e1veis que recebem um valor de entrada e podem ter seu conte\u00fado modificado, sendo retornadas ao final da execu\u00e7\u00e3o.<\/p>\n<p>Vamos criar uma fun\u00e7\u00e3o de exemplo que utiliza todos os tr\u00eas modos simultaneamente. A fun\u00e7\u00e3o <strong>analisar_produto<\/strong> recebe um ID, retorna o nome do produto (OUT), calcula a margem (OUT) e utiliza um par\u00e2metro INOUT para modificar o pre\u00e7o original in-place, demonstrando cada modo de par\u00e2metro na pr\u00e1tica.<\/p>\n<pre><code>-- Fun\u00e7\u00e3o com m\u00faltiplos modos de par\u00e2metros\nCREATE OR REPLACE FUNCTION analisar_produto(\n    IN  p_id INTEGER,              -- Par\u00e2metro de entrada (ID do produto)\n    OUT p_nome VARCHAR,            -- Par\u00e2metro de sa\u00edda (nome do produto)\n    INOUT p_preco NUMERIC,          -- Entrada e sa\u00edda (pre\u00e7o modificado)\n    OUT p_margem NUMERIC           -- Sa\u00edda (margem calculada)\n)\nLANGUAGE plpgsql\nSTABLE  -- Fun\u00e7\u00e3o est\u00e1vel: l\u00ea dados do banco mas n\u00e3o os modifica dentro do SELECT\nAS $$\nDECLARE\n    -- Vari\u00e1vel local para armazenar o pre\u00e7o de custo original\n    v_preco_original NUMERIC;\nBEGIN\n    -- Buscar dados do produto pelo ID\n    SELECT nome, preco INTO p_nome, v_preco_original\n    FROM produtos\n    WHERE id = p_id;\n\n    -- Se o produto n\u00e3o for encontrado, levantar exce\u00e7\u00e3o amig\u00e1vel\n    IF NOT FOUND THEN\n        RAISE EXCEPTION 'Produto com ID % n\u00e3o encontrado', p_id;\n    END IF;\n\n    -- Calcular novo pre\u00e7o com margem de 25% sobre o pre\u00e7o original\n    p_preco := ROUND(v_preco_original * 1.25, 2);\n    \n    -- Calcular a margem em valor monet\u00e1rio\n    p_margem := ROUND(p_preco - v_preco_original, 2);\n    \n    -- O RETURN \u00e9 impl\u00edcito ao final do bloco quando h\u00e1 par\u00e2metros OUT\/INOUT\nEND;\n$$;\n<\/code><\/pre>\n<p>Para testar esta fun\u00e7\u00e3o, utilizamos uma sintaxe ligeiramente diferente da anterior. Como ela possui m\u00faltiplos par\u00e2metros de retorno, a chamada deve ser feita com <code>SELECT * FROM analisar_produto(1)<\/code>. O asterisco captura todas as colunas de retorno definidas pelos par\u00e2metros <code>OUT<\/code> e <code>INOUT<\/code>.<\/p>\n<pre><code>-- Chamada da fun\u00e7\u00e3o com captura de todas as colunas de retorno\nSELECT * FROM analisar_produto(1);\n\n-- Chamada capturando colunas espec\u00edficas (\u00fatil para leitura)\nSELECT p_nome, p_margem FROM analisar_produto(1);\n<\/code><\/pre>\n<pre><code class=\"output\">-- Sa\u00edda de SELECT * FROM analisar_produto(1);\n p_preco |     p_nome      | p_margem\n---------+-----------------+----------\n  437.38 | Teclado Mec\u00e2nico RGB |    87.48\n(1 row)\n\n-- Sa\u00edda de SELECT p_nome, p_margem FROM analisar_produto(1);\n     p_nome      | p_margem\n-----------------+----------\n Teclado Mec\u00e2nico RGB |    87.48\n(1 row)\n<\/code><\/pre>\n<h3>Dominando Estruturas de Controle Condicionais \u2014 IF, CASE e a L\u00f3gica do Neg\u00f3cio<\/h3>\n<p>O verdadeiro poder da programa\u00e7\u00e3o procedural se manifesta quando voc\u00ea introduz ramifica\u00e7\u00f5es l\u00f3gicas que reagem aos dados. O PL\/pgSQL oferece constru\u00e7\u00f5es condicionais cl\u00e1ssicas encontradas em linguagens como Pascal ou Ada, mas perfeitamente integradas ao ambiente SQL. Dominar <code>IF-THEN-ELSE<\/code> e <code>CASE<\/code> \u00e9 essencial para criar <strong>functions e stored procedures<\/strong> que encapsulam regras de neg\u00f3cio complexas de forma coesa e audit\u00e1vel.<\/p>\n<p>A estrutura <code>IF<\/code> no PL\/pgSQL suporta m\u00faltiplas ramifica\u00e7\u00f5es com <code>ELSIF<\/code> (aten\u00e7\u00e3o \u00e0 grafia: n\u00e3o \u00e9 ELSEIF, mas <strong>ELSIF<\/strong>). Voc\u00ea pode aninhar blocos <code>IF<\/code> conforme necess\u00e1rio e sempre deve encerrar com <code>END IF;<\/code> seguido de ponto e v\u00edrgula. J\u00e1 o <code>CASE<\/code> existe em duas variantes: a express\u00e3o <code>CASE<\/code> (que retorna um valor e pode ser usada em atribui\u00e7\u00f5es) e o bloco <code>CASE-WHEN<\/code> procedimental, similar ao switch de outras linguagens.<\/p>\n<p>Vamos construir uma fun\u00e7\u00e3o de classifica\u00e7\u00e3o de produtos que demonstre ambas as estruturas de controle. A fun\u00e7\u00e3o <strong>classificar_produto<\/strong> receber\u00e1 um ID e retornar\u00e1 uma tabela com informa\u00e7\u00f5es de categoria, pre\u00e7o e uma classifica\u00e7\u00e3o textual baseada em regras de neg\u00f3cio pr\u00e9-definidas. Esta \u00e9 uma t\u00e9cnica comum em nossos projetos na JRT Technology Solutions para padronizar regras de categoriza\u00e7\u00e3o que s\u00e3o consumidas por m\u00faltiplos sistemas.<\/p>\n<pre><code>-- Fun\u00e7\u00e3o com estruturas condicionais IF e CASE para classifica\u00e7\u00e3o\nCREATE OR REPLACE FUNCTION classificar_produto(\n    p_id INTEGER\n)\nRETURNS TABLE (\n    nome_produto VARCHAR,\n    categoria VARCHAR,\n    preco_atual NUMERIC,\n    classificacao TEXT,\n    recomendacao VARCHAR\n)\nLANGUAGE plpgsql\nSTABLE\nAS $$\nDECLARE\n    v_nome VARCHAR;\n    v_cat VARCHAR;\n    v_preco NUMERIC;\n    v_estoque INTEGER;\n    v_classif TEXT;\n    v_recomend VARCHAR;\nBEGIN\n    -- Obter dados do produto (se n\u00e3o existir, retorna conjunto vazio)\n    SELECT nome, categoria, preco, estoque\n    INTO v_nome, v_cat, v_preco, v_estoque\n    FROM produtos\n    WHERE id = p_id;\n\n    -- Se n\u00e3o encontrou, retornar imediatamente (tabela vazia)\n    IF NOT FOUND THEN\n        RETURN;\n    END IF;\n\n    -- Classifica\u00e7\u00e3o prim\u00e1ria usando IF aninhados (pre\u00e7o)\n    IF v_preco < 300 THEN\n        v_classif := 'Econ\u00f4mico';\n    ELSIF v_preco >= 300 AND v_preco < 1000 THEN\n        v_classif := 'Intermedi\u00e1rio';\n    ELSIF v_preco >= 1000 AND v_preco < 3000 THEN\n        v_classif := 'Premium';\n    ELSE\n        v_classif := 'Luxo';\n    END IF;\n\n    -- Recomenda\u00e7\u00e3o usando CASE (express\u00e3o que retorna valor)\n    v_recomend := CASE v_cat\n        WHEN 'Perif\u00e9ricos' THEN \n            CASE \n                WHEN v_estoque > 80 THEN 'Comprar em lote com desconto'\n                WHEN v_estoque BETWEEN 30 AND 80 THEN 'Reposi\u00e7\u00e3o normal'\n                ELSE 'Estoque cr\u00edtico - prioridade alta'\n            END\n        WHEN 'Monitores' THEN\n            CASE\n                WHEN v_preco > 2000 THEN 'Oferecer garantia estendida'\n                ELSE 'Venda padr\u00e3o'\n            END\n        WHEN 'Computadores' THEN 'Agendar demonstra\u00e7\u00e3o t\u00e9cnica'\n        WHEN 'Armazenamento' THEN 'Sugerir upgrade de capacidade'\n        WHEN 'Redes' THEN 'Verificar compatibilidade com infraestrutura'\n        WHEN 'M\u00f3veis' THEN 'Confirmar disponibilidade de montagem'\n        ELSE 'An\u00e1lise manual necess\u00e1ria'\n    END;\n\n    -- Preencher a tabela de retorno\n    nome_produto := v_nome;\n    categoria := v_cat;\n    preco_atual := v_preco;\n    classificacao := v_classif;\n    recomendacao := v_recomend;\n\n    RETURN NEXT; -- Adiciona uma linha ao conjunto de resultados\nEND;\n$$;\n<\/code><\/pre>\n<p>Para verificar o funcionamento, vamos chamar a fun\u00e7\u00e3o para alguns produtos e observar como as regras de neg\u00f3cio s\u00e3o aplicadas consistentemente.<\/p>\n<pre><code>-- Testar a fun\u00e7\u00e3o de classifica\u00e7\u00e3o para diferentes IDs\nSELECT * FROM classificar_produto(1);\nSELECT * FROM classificar_produto(3);\nSELECT * FROM classificar_produto(4);\nSELECT * FROM classificar_produto(7);\n<\/code><\/pre>\n<pre><code class=\"output\">-- Sa\u00edda para produto ID 1 (Teclado Mec\u00e2nico RGB)\n    nome_produto     |  categoria  | preco_atual |  classificacao  |       recomendacao\n---------------------+-------------+-------------+-----------------+---------------------------\n Teclado Mec\u00e2nico RGB | Perif\u00e9ricos |      349.90 | Intermedi\u00e1rio   | Reposi\u00e7\u00e3o normal\n(1 row)\n\n-- Sa\u00edda para produto ID 3 (Monitor 27\" 4K)\n  nome_produto   | categoria | preco_atual | classificacao |      recomendacao\n-----------------+-----------+-------------+---------------+-------------------------\n Monitor 27\" 4K  | Monitores |     2499.00 | Premium       | Oferecer garantia estendida\n(1 row)\n\n-- Sa\u00edda para produto ID 4 (Notebook i7 16GB)\n   nome_produto   |  categoria   | preco_atual | classificacao |        recomendacao\n------------------+--------------+-------------+---------------+-----------------------------\n Notebook i7 16GB | Computadores |     5499.00 | Luxo          | Agendar demonstra\u00e7\u00e3o t\u00e9cnica\n(1 row)\n\n-- Sa\u00edda para produto ID 7 (Headset Surround)\n   nome_produto    |  categoria  | preco_atual |  classificacao  |       recomendacao\n-------------------+-------------+-------------+-----------------+---------------------------\n Headset Surround  | Perif\u00e9ricos |      399.90 | Intermedi\u00e1rio   | Reposi\u00e7\u00e3o normal\n(1 row)\n<\/code><\/pre>\n<h3>La\u00e7os de Repeti\u00e7\u00e3o e Itera\u00e7\u00e3o sobre Conjuntos \u2014 LOOP, WHILE e FOR<\/h3>\n<p>Processar dados linha a linha pode ser um antipadr\u00e3o em SQL se feito desnecessariamente, mas h\u00e1 cen\u00e1rios leg\u00edtimos onde a itera\u00e7\u00e3o procedural \u00e9 a melhor (ou \u00fanica) abordagem. O PL\/pgSQL oferece tr\u00eas constru\u00e7\u00f5es principais de la\u00e7o: <strong>LOOP\/EXIT<\/strong> (la\u00e7o infinito controlado manualmente), <strong>WHILE<\/strong> (la\u00e7o com condi\u00e7\u00e3o de entrada) e <strong>FOR<\/strong> (itera\u00e7\u00e3o sobre ranges num\u00e9ricos ou, mais importante, sobre resultados de consultas com cursor impl\u00edcito).<\/p>\n<p>O la\u00e7o <code>FOR<\/code> com cursor impl\u00edcito merece aten\u00e7\u00e3o especial. Sua sintaxe <code>FOR variavel IN SELECT ... LOOP ... END LOOP;<\/code> abstrai toda a complexidade de cursores, abrindo-os, iterando e fechando automaticamente. Internamente, o PostgreSQL utiliza cursores de forma otimizada, mas voc\u00ea nunca precisa declar\u00e1-los explicitamente. Essa \u00e9, de longe, a forma mais idiom\u00e1tica e segura de iterar sobre conjuntos de registros em <strong>functions e stored procedures<\/strong>.<\/p>\n<p>Vamos criar um procedimento armazenado (finalmente usando <code>CREATE PROCEDURE<\/code>) que itera sobre todos os produtos de uma categoria e aplica um reajuste percentual no pre\u00e7o, registrando cada altera\u00e7\u00e3o em uma tabela de auditoria. Este exemplo tamb\u00e9m introduzir\u00e1 o controle transacional parcial com <code>COMMIT<\/code> a cada lote de atualiza\u00e7\u00f5es, demonstrando a principal vantagem dos <strong>stored procedures<\/strong>.<\/p>\n<pre><code>-- Criar tabela de auditoria para registrar altera\u00e7\u00f5es de pre\u00e7os\nCREATE TABLE auditoria_precos (\n    id SERIAL PRIMARY KEY,\n    produto_id INTEGER NOT NULL,\n    preco_antigo NUMERIC(10,2),\n    preco_novo NUMERIC(10,2),\n    percentual_reajuste NUMERIC(5,2),\n    usuario_banco TEXT DEFAULT current_user,\n    data_alteracao TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP\n);\n\n-- Stored Procedure com la\u00e7o FOR e controle transacional expl\u00edcito\nCREATE OR REPLACE PROCEDURE reajustar_precos_categoria(\n    p_categoria VARCHAR,\n    p_percentual NUMERIC,\n    p_tamanho_lote INTEGER DEFAULT 50\n)\nLANGUAGE plpgsql\nAS $$\nDECLARE\n    v_reg RECORD;               -- Vari\u00e1vel record para armazenar linha da itera\u00e7\u00e3o\n    v_contador INTEGER := 0;    -- Contador de registros processados no lote atual\n    v_total_afetados INTEGER := 0; -- Total acumulado de registros alterados\n    v_preco_novo NUMERIC;\nBEGIN\n    -- La\u00e7o FOR com cursor impl\u00edcito: itera sobre o resultado da consulta\n    FOR v_reg IN \n        SELECT id, nome, preco \n        FROM produtos \n        WHERE categoria = p_categoria \n        ORDER BY id\n    LOOP\n        -- Calcular novo pre\u00e7o arredondado\n        v_preco_novo := ROUND(v_reg.preco * (1 + p_percentual \/ 100.0), 2);\n        \n        -- Atualizar o produto e registrar na auditoria em uma \u00fanica transa\u00e7\u00e3o l\u00f3gica\n        UPDATE produtos \n        SET preco = v_preco_novo \n        WHERE id = v_reg.id;\n\n        INSERT INTO auditoria_precos (produto_id, preco_antigo, preco_novo, percentual_reajuste)\n        VALUES (v_reg.id, v_reg.preco, v_preco_novo, p_percentual);\n\n        v_contador := v_contador + 1;\n        v_total_afetados := v_total_afetados + 1;\n\n        -- Controle transacional: commit a cada lote de p_tamanho_lote registros\n        IF v_contador >= p_tamanho_lote THEN\n            COMMIT; -- Confirma o lote processado (libera locks e visibilidade)\n            RAISE NOTICE 'Lote de % registros confirmado. Total acumulado: %', v_contador, v_total_afetados;\n            v_contador := 0;\n        END IF;\n    END LOOP;\n\n    -- Confirma o lote residual (menor que p_tamanho_lote)\n    IF v_contador > 0 THEN\n        COMMIT;\n        RAISE NOTICE 'Lote final de % registros confirmado. Total acumulado: %', v_contador, v_total_afetados;\n    END IF;\n\n    RAISE NOTICE 'Procedimento conclu\u00eddo. Total de registros alterados: %', v_total_afetados;\nEND;\n$$;\n<\/code><\/pre>\n<p>Para executar esse procedimento, utilizamos o comando <code>CALL<\/code>, n\u00e3o <code>SELECT<\/code>. Repare que podemos chamar o procedimento com os par\u00e2metros posicionais ou nomeados. Vamos aplicar um reajuste de 10% para a categoria &#8216;Perif\u00e9ricos&#8217; com lotes de 2 registros para demonstrar o commit em lote.<\/p>\n<pre><code>-- Chamada do stored procedure com lote pequeno para demonstra\u00e7\u00e3o\nCALL reajustar_precos_categoria('Perif\u00e9ricos', 10.0, 2);\n\n-- Verificar os novos pre\u00e7os da categoria\nSELECT nome, preco FROM produtos WHERE categoria = 'Perif\u00e9ricos';\n\n-- Verificar registros de auditoria gerados\nSELECT produto_id, preco_antigo, preco_novo, percentual_reajuste \nFROM auditoria_precos \nORDER BY id;\n<\/code><\/pre>\n<pre><code class=\"output\">-- NOTICE exibidos durante a execu\u00e7\u00e3o do CALL:\nNOTICE:  Lote de 2 registros confirmado. Total acumulado: 2\nNOTICE:  Lote final de 1 registros confirmado. Total acumulado: 3\nNOTICE:  Procedimento conclu\u00eddo. Total de registros alterados: 3\nCALL\n\n-- Novos pre\u00e7os (acr\u00e9scimo de 10%):\n         nome          |  preco\n------------------------+---------\n Teclado Mec\u00e2nico RGB   |  384.89\n Mouse Gamer 16000DPI   |  274.89\n Headset Surround       |  439.89\n(3 rows)\n\n-- Registros de auditoria:\n produto_id | preco_antigo | preco_novo | percentual_reajuste\n------------+--------------+------------+---------------------\n          1 |       349.90 |     384.89 |               10.00\n          2 |       249.90 |     274.89 |               10.00\n          7 |       399.90 |     439.89 |               10.00\n(3 rows)\n<\/code><\/pre>\n<p>Este exemplo demonstra uma aplica\u00e7\u00e3o real de controle transacional dentro de <strong>stored procedures<\/strong>. Se ocorrer um erro ap\u00f3s alguns commits, o trabalho j\u00e1 confirmado permanece persistido \u2014 diferentemente de uma <strong>function<\/strong>, onde todo o trabalho seria desfeito. Em nossos projetos na JRT Technology Solutions, utilizamos essa t\u00e9cnica para processamento de grandes volumes de dados onde a atomicidade total seria proibitiva devido ao tamanho da transa\u00e7\u00e3o e ao risco de locks prolongados.<\/p>\n<h3>Fun\u00e7\u00f5es com Retorno de Tabelas Din\u00e2micas \u2014 RETURNS TABLE e SETOF<\/h3>\n<p>Uma das capacidades mais valorizadas em <strong>functions<\/strong> \u00e9 a habilidade de retornar conjuntos de registros \u2014 essencialmente, tabelas geradas dinamicamente que podem ser consumidas como qualquer outra rela\u00e7\u00e3o em consultas SQL. O PostgreSQL oferece duas sintaxes principais: <strong>RETURNS SETOF tipo_tabela<\/strong> (ou <strong>SETOF record<\/strong>) e <strong>RETURNS TABLE (colunas)<\/strong>. A segunda \u00e9 geralmente mais expressiva e autodocumentada, pois declara explicitamente a estrutura de sa\u00edda.<\/p>\n<p>Internamente, fun\u00e7\u00f5es que retornam conjuntos utilizam o mecanismo de <strong>RETURN NEXT<\/strong> (ou <strong>RETURN QUERY<\/strong>) para emitir linhas incrementais. <code>RETURN NEXT<\/code> adiciona uma linha ao conjunto de resultados, mas n\u00e3o encerra a execu\u00e7\u00e3o \u2014 voc\u00ea pode continuar processando e adicionar mais linhas. J\u00e1 <code>RETURN QUERY<\/code> executa uma consulta inteira e anexa todos os seus resultados ao conjunto de retorno de uma s\u00f3 vez. Essas constru\u00e7\u00f5es podem ser combinadas livremente dentro da mesma fun\u00e7\u00e3o.<\/p>\n<p>Vamos criar uma fun\u00e7\u00e3o de relat\u00f3rio flex\u00edvel que aceita filtros opcionais de categoria e faixa de pre\u00e7o, retornando uma tabela personalizada com indicadores calculados. Este tipo de fun\u00e7\u00e3o \u00e9 extremamente \u00fatil para dashboards e APIs que precisam de dados agregados sem expor consultas complexas ao frontend.<\/p>\n<pre><code>-- Function com RETURNS TABLE e m\u00faltiplos RETURN QUERY\nCREATE OR REPLACE FUNCTION relatorio_estoque(\n    p_categoria VARCHAR DEFAULT NULL,   -- Par\u00e2metro opcional: filtrar por categoria\n    p_preco_min NUMERIC DEFAULT NULL,   -- Par\u00e2metro opcional: pre\u00e7o m\u00ednimo\n    p_preco_max NUMERIC DEFAULT NULL    -- Par\u00e2metro opcional: pre\u00e7o m\u00e1ximo\n)\nRETURNS TABLE (\n    faixa_preco TEXT,\n    total_produtos BIGINT,\n    estoque_total BIGINT,\n    valor_estoque NUMERIC,\n    preco_medio NUMERIC,\n    categorias_incluidas TEXT\n)\nLANGUAGE plpgsql\nSTABLE\nAS $$\nBEGIN\n    -- Primeira faixa: produtos com pre\u00e7o baixo (conforme filtros)\n    RETURN QUERY\n    SELECT \n        'Baixo (at\u00e9 R$ 500)'::TEXT,\n        count(*)::BIGINT,\n        COALESCE(sum(estoque), 0)::BIGINT,\n        COALESCE(sum(preco * estoque), 0),\n        ROUND(COALESCE(avg(preco), 0), 2),\n        string_agg(DISTINCT categoria, ', ' ORDER BY categoria)\n    FROM produtos\n    WHERE (p_categoria IS NULL OR categoria = p_categoria)\n      AND (p_preco_min IS NULL OR preco >= p_preco_min)\n      AND (p_preco_max IS NULL OR preco <= p_preco_max)\n      AND preco <= 500;\n\n    -- Segunda faixa: produtos com pre\u00e7o m\u00e9dio\n    RETURN QUERY\n    SELECT \n        'M\u00e9dio (R$ 501 a R$ 2000)'::TEXT,\n        count(*)::BIGINT,\n        COALESCE(sum(estoque), 0)::BIGINT,\n        COALESCE(sum(preco * estoque), 0),\n        ROUND(COALESCE(avg(preco), 0), 2),\n        string_agg(DISTINCT categoria, ', ' ORDER BY categoria)\n    FROM produtos\n    WHERE (p_categoria IS NULL OR categoria = p_categoria)\n      AND (p_preco_min IS NULL OR preco >= p_preco_min)\n      AND (p_preco_max IS NULL OR preco <= p_preco_max)\n      AND preco BETWEEN 501 AND 2000;\n\n    -- Terceira faixa: produtos premium\n    RETURN QUERY\n    SELECT \n        'Alto (acima de R$ 2000)'::TEXT,\n        count(*)::BIGINT,\n        COALESCE(sum(estoque), 0)::BIGINT,\n        COALESCE(sum(preco * estoque), 0),\n        ROUND(COALESCE(avg(preco), 0), 2),\n        string_agg(DISTINCT categoria, ', ' ORDER BY categoria)\n    FROM produtos\n    WHERE (p_categoria IS NULL OR categoria = p_categoria)\n      AND (p_preco_min IS NULL OR preco >= p_preco_min)\n      AND (p_preco_max IS NULL OR preco <= p_preco_max)\n      AND preco > 2000;\nEND;\n$$;\n<\/code><\/pre>\n<p>Para consumir essa fun\u00e7\u00e3o, utilizamos um simples <code>SELECT * FROM relatorio_estoque()<\/code>, podendo inclusive aplicar cl\u00e1usulas <code>WHERE<\/code> e <code>ORDER BY<\/code> adicionais sobre o resultado. Veja como \u00e9 pr\u00e1tico:<\/p>\n<pre><code>-- Relat\u00f3rio completo (todas as categorias e faixas)\nSELECT * FROM relatorio_estoque();\n\n-- Relat\u00f3rio filtrado por categoria (apenas Perif\u00e9ricos)\nSELECT * FROM relatorio_estoque(p_categoria := 'Perif\u00e9ricos');\n\n-- Relat\u00f3rio com m\u00faltiplos filtros combinados\nSELECT faixa_preco, total_produtos, valor_estoque\nFROM relatorio_estoque(p_preco_min := 100, p_preco_max := 3000)\nORDER BY valor_estoque DESC;\n<\/code><\/pre>\n<pre><code class=\"output\">-- Sa\u00edda parcial de SELECT * FROM relatorio_estoque();\n        faixa_preco        | total_produtos | estoque_total | valor_estoque | preco_medio |      categorias_incluidas\n---------------------------+----------------+---------------+---------------+-------------+---------------------------------\n Baixo (at\u00e9 R$ 500)        |              5 |           465 |     173883.50 |      379.50 | Armazenamento, Perif\u00e9ricos, Redes\n M\u00e9dio (R$ 501 a R$ 2000)  |              2 |           210 |     392580.00 |      949.45 | Cadeira Gamer, Perif\u00e9ricos\n Alto (acima de R$ 2000)   |              1 |            15 |      37485.00 |     2499.00 | Monitores\n(3 rows)\n<\/code><\/pre>\n<h3>Triggers e Functions \u2014 Automatizando Rea\u00e7\u00f5es a Eventos de Dados<\/h3>\n<p>Triggers s\u00e3o um dos casos de uso mais comuns para <strong>functions<\/strong> no PostgreSQL. Um trigger \u00e9 um gatilho associado a uma tabela que dispara automaticamente antes ou depois de opera\u00e7\u00f5es <code>INSERT<\/code>, <code>UPDATE<\/code>, <code>DELETE<\/code> ou <code>TRUNCATE<\/code>. A fun\u00e7\u00e3o executada pelo trigger tem acesso a vari\u00e1veis especiais como <strong>NEW<\/strong> (registro ap\u00f3s a opera\u00e7\u00e3o em INSERT\/UPDATE) e <strong>OLD<\/strong> (registro antes da opera\u00e7\u00e3o em UPDATE\/DELETE), e pode inclusive modificar o valor de <code>NEW<\/code> em triggers <code>BEFORE<\/code> para alterar o dado que ser\u00e1 efetivamente gravado.<\/p>\n<p>A assinatura de uma fun\u00e7\u00e3o de trigger \u00e9 fixa: ela deve retornar <code>TRIGGER<\/code> (um tipo especial) e n\u00e3o aceita par\u00e2metros expl\u00edcitos \u2014 as informa\u00e7\u00f5es de contexto v\u00eam atrav\u00e9s de <code>TG_OP<\/code>, <code>TG_TABLE_NAME<\/code>, <code>NEW<\/code> e <code>OLD<\/code>. Vamos criar um sistema de auditoria autom\u00e1tica que registra todas as altera\u00e7\u00f5es de pre\u00e7o na tabela de produtos sem que a aplica\u00e7\u00e3o precise fazer nada al\u00e9m de seu trabalho normal. Este \u00e9 um padr\u00e3o que implementamos recorrentemente na JRT Technology Solutions para compliance e rastreabilidade.<\/p>\n<pre><code>-- Function espec\u00edfica para ser usada como trigger\nCREATE OR REPLACE FUNCTION trigger_auditar_alteracao_preco()\nRETURNS TRIGGER\nLANGUAGE plpgsql\nSECURITY DEFINER  -- Executa com privil\u00e9gios do criador da fun\u00e7\u00e3o\nAS $$\nBEGIN\n    -- Conforme a opera\u00e7\u00e3o, registramos informa\u00e7\u00f5es apropriadas na auditoria\n    IF TG_OP = 'INSERT' THEN\n        INSERT INTO auditoria_precos (produto_id, preco_antigo, preco_novo, percentual_reajuste)\n        VALUES (NEW.id, NULL, NEW.preco, 0);\n        RETURN NEW;\n        \n    ELSIF TG_OP = 'UPDATE' THEN\n        -- S\u00f3 registra se o pre\u00e7o realmente mudou\n        IF OLD.preco IS DISTINCT FROM NEW.preco THEN\n            INSERT INTO auditoria_precos (produto_id, preco_antigo, preco_novo, percentual_reajuste)\n            VALUES (\n                NEW.id,\n                OLD.preco,\n                NEW.preco,\n                CASE \n                    WHEN OLD.preco > 0 THEN ROUND((NEW.preco - OLD.preco) \/ OLD.preco * 100, 2)\n                    ELSE NULL\n                END\n            );\n        END IF;\n        RETURN NEW;\n        \n    ELSIF TG_OP = 'DELETE' THEN\n        INSERT INTO auditoria_precos (produto_id, preco_antigo, preco_novo, percentual_reajuste)\n        VALUES (OLD.id, OLD.preco, NULL, NULL);\n        RETURN OLD;\n    END IF;\n    \n    -- Caso ocorra algum fluxo inesperado (n\u00e3o deve ocorrer)\n    RETURN NULL;\nEND;\n$$;\n\n-- Criar o trigger associado \u00e0 tabela produtos\nCREATE TRIGGER trg_auditar_produtos\n    AFTER INSERT OR UPDATE OR DELETE ON produtos\n    FOR EACH ROW\n    EXECUTE FUNCTION trigger_auditar_alteracao_preco();\n<\/code><\/pre>\n<p>Agora, vamos testar o trigger realizando opera\u00e7\u00f5es comuns de manipula\u00e7\u00e3o de dados e verificando se a tabela de auditoria reflete automaticamente cada altera\u00e7\u00e3o.<\/p>\n<pre><code>-- Inserir um novo produto (dispara trigger AFTER INSERT)\nINSERT INTO produtos (nome, categoria, preco, estoque)\nVALUES ('Webcam Full HD', 'Perif\u00e9ricos', 199.90, 45);\n\n-- Atualizar o pre\u00e7o de um produto existente (dispara AFTER UPDATE)\nUPDATE produtos SET preco = 279.90 WHERE nome = 'Mouse Gamer 16000DPI';\n\n-- Deletar um produto (dispara AFTER DELETE)\nDELETE FROM produtos WHERE nome = 'Cadeira Gamer';\n\n-- Consultar a tabela de auditoria para ver os registros autom\u00e1ticos\nSELECT \n    id,\n    produto_id,\n    preco_antigo,\n    preco_novo,\n    percentual_reajuste,\n    data_alteracao\nFROM auditoria_precos\nORDER BY id DESC\nLIMIT 5;\n<\/code><\/pre>\n<pre><code class=\"output\"> id | produto_id | preco_antigo | preco_novo | percentual_reajuste |        data_alteracao\n----+------------+--------------+------------+---------------------+-------------------------------\n  9 |          8 |      1299.00 |            |                     | 2026-07-10 14:30:22.123456-03\n 10 |          2 |       274.89 |     279.90 |                1.82 | 2026-07-10 14:30:22.124789-03\n 11 |          9 |              |     199.90 |                0.00 | 2026-07-10 14:30:22.125012-03\n(3 rows)\n<\/code><\/pre>\n<p>Repare na eleg\u00e2ncia dessa solu\u00e7\u00e3o: a aplica\u00e7\u00e3o que faz o <code\n\n\n<div style=\"margin:52px 0 40px;padding:36px 28px;background:linear-gradient(135deg,#0f172a 0%,#1a2744 100%);border:2px solid #25D366;border-radius:18px;text-align:center;box-shadow:0 4px 28px rgba(37,211,102,0.18)\">\n<p style=\"margin:0 0 10px;font-size:18px;color:#ffffff;font-weight:700;line-height:1.4\">Quer aprender na pr\u00e1tica com especialistas?<\/p>\n<p style=\"margin:0 0 28px;font-size:15px;color:#94a3b8;font-weight:400;line-height:1.6\">A JRT Technology Solutions oferece treinamentos e implementa\u00e7\u00e3o de PostgreSQL para equipes corporativas.<\/p>\n<p>  <a href=\"https:\/\/api.whatsapp.com\/send\/?phone=5521980606699&#038;text=Ol%C3%A1!%20Tenho%20interesse%20no%20treinamento%20de%20PostgreSQL.&#038;type=phone_number&#038;app_absent=0\"\n     target=\"_blank\" rel=\"noopener noreferrer\"\n     style=\"display:inline-flex;align-items:center;gap:12px;background:#25D366;color:#ffffff;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:16px;font-weight:700;padding:15px 32px;border-radius:100px;text-decoration:none;box-shadow:0 4px 16px rgba(37,211,102,0.45);letter-spacing:0.01em\"><br \/>\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"#ffffff\"><path d=\"M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z\"\/><\/svg><br \/>\n    Falar no WhatsApp<br \/>\n  <\/a>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Domine Functions e Stored Procedures em PL\/pgSQL no PostgreSQL. Aprenda a l\u00f3gica essencial para otimizar seu banco de dados. Clique e descubra!<\/p>\n","protected":false},"author":1,"featured_media":1463,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"iawp_total_views":3,"footnotes":""},"categories":[75],"tags":[1743,2411,2410,87,2412,2413],"class_list":["post-1464","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops","tag-banco-de-dados","tag-funcoes-em-postgresql","tag-pl-pgsql","tag-postgresql","tag-procedimentos-armazenados","tag-programacao-em-banco-de-dados"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"Domine Functions e Stored Procedures em PL\/pgSQL no PostgreSQL. Aprenda a l\u00f3gica essencial para otimizar seu banco de dados. Clique e descubra!\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Thiago Paes Rodrigues\"\/>\n\t<meta name=\"google-site-verification\" content=\"QKPfpDCzHmzKyfFk5j1KZW3HhROYTPeqwBN5qcJ8qcg\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"pt_BR\" \/>\n\t\t<meta property=\"og:site_name\" content=\"BLOG - JRT Technology Solutions - Um espa\u00e7o dedicado a explorar o universo da tecnologia de forma clara, pr\u00e1tica e atual. Aqui voc\u00ea encontra novidades, an\u00e1lises, tutoriais e reflex\u00f5es sobre inova\u00e7\u00e3o, intelig\u00eancia artificial, gadgets, programa\u00e7\u00e3o e tend\u00eancias digitais. Nosso objetivo \u00e9 simplificar o complexo, ajudar voc\u00ea a se manter atualizado e transformar conhecimento em pr\u00e1tica no seu dia a dia.\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Aula 16: Functions e Stored Procedures em PL\/pgSQL \u2014 Domine a L\u00f3gica no Banco de Dados - BLOG - JRT Technology Solutions\" \/>\n\t\t<meta property=\"og:description\" content=\"Domine Functions e Stored Procedures em PL\/pgSQL no PostgreSQL. Aprenda a l\u00f3gica essencial para otimizar seu banco de dados. Clique e descubra!\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/jrtx.com.br\/blog\/wp-content\/uploads\/2026\/03\/cropped-logo-mini.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/jrtx.com.br\/blog\/wp-content\/uploads\/2026\/03\/cropped-logo-mini.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"100\" \/>\n\t\t<meta property=\"og:image:height\" content=\"75\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2026-07-10T20:51:27+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-07-10T20:51:27+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/profile.php?id=61590814880509\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Aula 16: Functions e Stored Procedures em PL\/pgSQL \u2014 Domine a L\u00f3gica no Banco de Dados - BLOG - JRT Technology Solutions\" \/>\n\t\t<meta name=\"twitter:description\" content=\"Domine Functions e Stored Procedures em PL\/pgSQL no PostgreSQL. Aprenda a l\u00f3gica essencial para otimizar seu banco de dados. Clique e descubra!\" \/>\n\t\t<meta name=\"twitter:image\" content=\"https:\/\/jrtx.com.br\/blog\/wp-content\/uploads\/2026\/03\/cropped-logo-mini.jpg\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"BlogPosting\",\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/10\\\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\\\/#blogposting\",\"name\":\"Aula 16: Functions e Stored Procedures em PL\\\/pgSQL \\u2014 Domine a L\\u00f3gica no Banco de Dados - BLOG - JRT Technology Solutions\",\"headline\":\"Aula 16: Functions e Stored Procedures em PL\\\/pgSQL \\u2014 Domine a L\\u00f3gica no Banco de Dados\",\"author\":{\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/author\\\/thiago\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/#organization\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/ai-image-1783716680263.jpg\",\"width\":1440,\"height\":1024,\"caption\":\"Aula 16: Functions e Stored Procedures em PL\\\/pgSQL \\u2014 Domine a L\\u00f3gica no Banco de Dados\"},\"datePublished\":\"2026-07-10T17:51:27-03:00\",\"dateModified\":\"2026-07-10T17:51:27-03:00\",\"inLanguage\":\"pt-BR\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/10\\\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/10\\\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\\\/#webpage\"},\"articleSection\":\"DevOps, banco de dados, fun\\u00e7\\u00f5es em PostgreSQL, PL\\\/pgSQL, PostgreSQL, procedimentos armazenados, programa\\u00e7\\u00e3o em banco de dados\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/10\\\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/jrtx.com.br\\\/blog\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/category\\\/devops\\\/#listItem\",\"name\":\"DevOps\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/category\\\/devops\\\/#listItem\",\"position\":2,\"name\":\"DevOps\",\"item\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/category\\\/devops\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/10\\\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\\\/#listItem\",\"name\":\"Aula 16: Functions e Stored Procedures em PL\\\/pgSQL \\u2014 Domine a L\\u00f3gica no Banco de Dados\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/10\\\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\\\/#listItem\",\"position\":3,\"name\":\"Aula 16: Functions e Stored Procedures em PL\\\/pgSQL \\u2014 Domine a L\\u00f3gica no Banco de Dados\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/category\\\/devops\\\/#listItem\",\"name\":\"DevOps\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/#organization\",\"name\":\"JRT Technology Solutions | Tecnologia e Inova\\u00e7\\u00e3o para Empresas\",\"description\":\"Um espa\\u00e7o dedicado a explorar o universo da tecnologia de forma clara, pr\\u00e1tica e atual. Aqui voc\\u00ea encontra novidades, an\\u00e1lises, tutoriais e reflex\\u00f5es sobre inova\\u00e7\\u00e3o, intelig\\u00eancia artificial, gadgets, programa\\u00e7\\u00e3o e tend\\u00eancias digitais. Nosso objetivo \\u00e9 simplificar o complexo, ajudar voc\\u00ea a se manter atualizado e transformar conhecimento em pr\\u00e1tica no seu dia a dia.\",\"url\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/\",\"telephone\":\"+552138277513\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/03\\\/cropped-logo-mini.jpg\",\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/10\\\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\\\/#organizationLogo\",\"width\":100,\"height\":75},\"image\":{\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/10\\\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\\\/#organizationLogo\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/profile.php?id=61590814880509\",\"https:\\\/\\\/www.instagram.com\\\/jrtx.tech\\\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/author\\\/thiago\\\/#author\",\"url\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/author\\\/thiago\\\/\",\"name\":\"Thiago Paes Rodrigues\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/10\\\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\\\/#authorImage\",\"url\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/cropped-foto-rascunho3-96x96.jpeg\",\"width\":96,\"height\":96,\"caption\":\"Thiago Paes Rodrigues\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/10\\\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\\\/#webpage\",\"url\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/10\\\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\\\/\",\"name\":\"Aula 16: Functions e Stored Procedures em PL\\\/pgSQL \\u2014 Domine a L\\u00f3gica no Banco de Dados - BLOG - JRT Technology Solutions\",\"description\":\"Domine Functions e Stored Procedures em PL\\\/pgSQL no PostgreSQL. Aprenda a l\\u00f3gica essencial para otimizar seu banco de dados. Clique e descubra!\",\"inLanguage\":\"pt-BR\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/10\\\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/author\\\/thiago\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/author\\\/thiago\\\/#author\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/ai-image-1783716680263.jpg\",\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/10\\\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\\\/#mainImage\",\"width\":1440,\"height\":1024,\"caption\":\"Aula 16: Functions e Stored Procedures em PL\\\/pgSQL \\u2014 Domine a L\\u00f3gica no Banco de Dados\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/10\\\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\\\/#mainImage\"},\"datePublished\":\"2026-07-10T17:51:27-03:00\",\"dateModified\":\"2026-07-10T17:51:27-03:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/\",\"name\":\"JRT Technology Solutions | Tecnologia e Inova\\u00e7\\u00e3o para Empresas\",\"alternateName\":\"JRT Technology Solutions | Tecnologia e Inova\\u00e7\\u00e3o para Empresas\",\"description\":\"Um espa\\u00e7o dedicado a explorar o universo da tecnologia de forma clara, pr\\u00e1tica e atual. Aqui voc\\u00ea encontra novidades, an\\u00e1lises, tutoriais e reflex\\u00f5es sobre inova\\u00e7\\u00e3o, intelig\\u00eancia artificial, gadgets, programa\\u00e7\\u00e3o e tend\\u00eancias digitais. Nosso objetivo \\u00e9 simplificar o complexo, ajudar voc\\u00ea a se manter atualizado e transformar conhecimento em pr\\u00e1tica no seu dia a dia.\",\"inLanguage\":\"pt-BR\",\"publisher\":{\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Aula 16: Functions e Stored Procedures em PL\/pgSQL \u2014 Domine a L\u00f3gica no Banco de Dados - BLOG - JRT Technology Solutions","description":"Domine Functions e Stored Procedures em PL\/pgSQL no PostgreSQL. Aprenda a l\u00f3gica essencial para otimizar seu banco de dados. Clique e descubra!","canonical_url":"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/","robots":"max-image-preview:large","keywords":"","webmasterTools":{"google-site-verification":"QKPfpDCzHmzKyfFk5j1KZW3HhROYTPeqwBN5qcJ8qcg","miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"BlogPosting","@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/#blogposting","name":"Aula 16: Functions e Stored Procedures em PL\/pgSQL \u2014 Domine a L\u00f3gica no Banco de Dados - BLOG - JRT Technology Solutions","headline":"Aula 16: Functions e Stored Procedures em PL\/pgSQL \u2014 Domine a L\u00f3gica no Banco de Dados","author":{"@id":"https:\/\/jrtx.com.br\/blog\/author\/thiago\/#author"},"publisher":{"@id":"https:\/\/jrtx.com.br\/blog\/#organization"},"image":{"@type":"ImageObject","url":"https:\/\/jrtx.com.br\/blog\/wp-content\/uploads\/2026\/07\/ai-image-1783716680263.jpg","width":1440,"height":1024,"caption":"Aula 16: Functions e Stored Procedures em PL\/pgSQL \u2014 Domine a L\u00f3gica no Banco de Dados"},"datePublished":"2026-07-10T17:51:27-03:00","dateModified":"2026-07-10T17:51:27-03:00","inLanguage":"pt-BR","mainEntityOfPage":{"@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/#webpage"},"isPartOf":{"@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/#webpage"},"articleSection":"DevOps, banco de dados, fun\u00e7\u00f5es em PostgreSQL, PL\/pgSQL, PostgreSQL, procedimentos armazenados, programa\u00e7\u00e3o em banco de dados"},{"@type":"BreadcrumbList","@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/jrtx.com.br\/blog#listItem","position":1,"name":"Home","item":"https:\/\/jrtx.com.br\/blog","nextItem":{"@type":"ListItem","@id":"https:\/\/jrtx.com.br\/blog\/category\/devops\/#listItem","name":"DevOps"}},{"@type":"ListItem","@id":"https:\/\/jrtx.com.br\/blog\/category\/devops\/#listItem","position":2,"name":"DevOps","item":"https:\/\/jrtx.com.br\/blog\/category\/devops\/","nextItem":{"@type":"ListItem","@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/#listItem","name":"Aula 16: Functions e Stored Procedures em PL\/pgSQL \u2014 Domine a L\u00f3gica no Banco de Dados"},"previousItem":{"@type":"ListItem","@id":"https:\/\/jrtx.com.br\/blog#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/#listItem","position":3,"name":"Aula 16: Functions e Stored Procedures em PL\/pgSQL \u2014 Domine a L\u00f3gica no Banco de Dados","previousItem":{"@type":"ListItem","@id":"https:\/\/jrtx.com.br\/blog\/category\/devops\/#listItem","name":"DevOps"}}]},{"@type":"Organization","@id":"https:\/\/jrtx.com.br\/blog\/#organization","name":"JRT Technology Solutions | Tecnologia e Inova\u00e7\u00e3o para Empresas","description":"Um espa\u00e7o dedicado a explorar o universo da tecnologia de forma clara, pr\u00e1tica e atual. Aqui voc\u00ea encontra novidades, an\u00e1lises, tutoriais e reflex\u00f5es sobre inova\u00e7\u00e3o, intelig\u00eancia artificial, gadgets, programa\u00e7\u00e3o e tend\u00eancias digitais. Nosso objetivo \u00e9 simplificar o complexo, ajudar voc\u00ea a se manter atualizado e transformar conhecimento em pr\u00e1tica no seu dia a dia.","url":"https:\/\/jrtx.com.br\/blog\/","telephone":"+552138277513","logo":{"@type":"ImageObject","url":"https:\/\/jrtx.com.br\/blog\/wp-content\/uploads\/2026\/03\/cropped-logo-mini.jpg","@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/#organizationLogo","width":100,"height":75},"image":{"@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/#organizationLogo"},"sameAs":["https:\/\/www.facebook.com\/profile.php?id=61590814880509","https:\/\/www.instagram.com\/jrtx.tech\/"]},{"@type":"Person","@id":"https:\/\/jrtx.com.br\/blog\/author\/thiago\/#author","url":"https:\/\/jrtx.com.br\/blog\/author\/thiago\/","name":"Thiago Paes Rodrigues","image":{"@type":"ImageObject","@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/#authorImage","url":"https:\/\/jrtx.com.br\/blog\/wp-content\/uploads\/2026\/09\/cropped-foto-rascunho3-96x96.jpeg","width":96,"height":96,"caption":"Thiago Paes Rodrigues"}},{"@type":"WebPage","@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/#webpage","url":"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/","name":"Aula 16: Functions e Stored Procedures em PL\/pgSQL \u2014 Domine a L\u00f3gica no Banco de Dados - BLOG - JRT Technology Solutions","description":"Domine Functions e Stored Procedures em PL\/pgSQL no PostgreSQL. Aprenda a l\u00f3gica essencial para otimizar seu banco de dados. Clique e descubra!","inLanguage":"pt-BR","isPartOf":{"@id":"https:\/\/jrtx.com.br\/blog\/#website"},"breadcrumb":{"@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/#breadcrumblist"},"author":{"@id":"https:\/\/jrtx.com.br\/blog\/author\/thiago\/#author"},"creator":{"@id":"https:\/\/jrtx.com.br\/blog\/author\/thiago\/#author"},"image":{"@type":"ImageObject","url":"https:\/\/jrtx.com.br\/blog\/wp-content\/uploads\/2026\/07\/ai-image-1783716680263.jpg","@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/#mainImage","width":1440,"height":1024,"caption":"Aula 16: Functions e Stored Procedures em PL\/pgSQL \u2014 Domine a L\u00f3gica no Banco de Dados"},"primaryImageOfPage":{"@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/#mainImage"},"datePublished":"2026-07-10T17:51:27-03:00","dateModified":"2026-07-10T17:51:27-03:00"},{"@type":"WebSite","@id":"https:\/\/jrtx.com.br\/blog\/#website","url":"https:\/\/jrtx.com.br\/blog\/","name":"JRT Technology Solutions | Tecnologia e Inova\u00e7\u00e3o para Empresas","alternateName":"JRT Technology Solutions | Tecnologia e Inova\u00e7\u00e3o para Empresas","description":"Um espa\u00e7o dedicado a explorar o universo da tecnologia de forma clara, pr\u00e1tica e atual. Aqui voc\u00ea encontra novidades, an\u00e1lises, tutoriais e reflex\u00f5es sobre inova\u00e7\u00e3o, intelig\u00eancia artificial, gadgets, programa\u00e7\u00e3o e tend\u00eancias digitais. Nosso objetivo \u00e9 simplificar o complexo, ajudar voc\u00ea a se manter atualizado e transformar conhecimento em pr\u00e1tica no seu dia a dia.","inLanguage":"pt-BR","publisher":{"@id":"https:\/\/jrtx.com.br\/blog\/#organization"}}]},"og:locale":"pt_BR","og:site_name":"BLOG - JRT Technology Solutions - Um espa\u00e7o dedicado a explorar o universo da tecnologia de forma clara, pr\u00e1tica e atual. Aqui voc\u00ea encontra novidades, an\u00e1lises, tutoriais e reflex\u00f5es sobre inova\u00e7\u00e3o, intelig\u00eancia artificial, gadgets, programa\u00e7\u00e3o e tend\u00eancias digitais. Nosso objetivo \u00e9 simplificar o complexo, ajudar voc\u00ea a se manter atualizado e transformar conhecimento em pr\u00e1tica no seu dia a dia.","og:type":"article","og:title":"Aula 16: Functions e Stored Procedures em PL\/pgSQL \u2014 Domine a L\u00f3gica no Banco de Dados - BLOG - JRT Technology Solutions","og:description":"Domine Functions e Stored Procedures em PL\/pgSQL no PostgreSQL. Aprenda a l\u00f3gica essencial para otimizar seu banco de dados. Clique e descubra!","og:url":"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/","og:image":"https:\/\/jrtx.com.br\/blog\/wp-content\/uploads\/2026\/03\/cropped-logo-mini.jpg","og:image:secure_url":"https:\/\/jrtx.com.br\/blog\/wp-content\/uploads\/2026\/03\/cropped-logo-mini.jpg","og:image:width":100,"og:image:height":75,"article:published_time":"2026-07-10T20:51:27+00:00","article:modified_time":"2026-07-10T20:51:27+00:00","article:publisher":"https:\/\/www.facebook.com\/profile.php?id=61590814880509","twitter:card":"summary_large_image","twitter:title":"Aula 16: Functions e Stored Procedures em PL\/pgSQL \u2014 Domine a L\u00f3gica no Banco de Dados - BLOG - JRT Technology Solutions","twitter:description":"Domine Functions e Stored Procedures em PL\/pgSQL no PostgreSQL. Aprenda a l\u00f3gica essencial para otimizar seu banco de dados. Clique e descubra!","twitter:image":"https:\/\/jrtx.com.br\/blog\/wp-content\/uploads\/2026\/03\/cropped-logo-mini.jpg"},"aioseo_meta_data":{"post_id":"1464","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_custom_url":null,"og_image_custom_fields":null,"og_image_url":null,"og_image_width":null,"og_image_height":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_image_url":null,"twitter_title":null,"twitter_description":null,"schema_type":"default","schema_type_options":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"limit_modified_date":false,"ai":null,"breadcrumb_settings":null,"seo_analyzer_scan_date":null,"created":"2026-07-10 20:58:39","updated":"2026-07-10 20:58:39","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/jrtx.com.br\/blog\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/jrtx.com.br\/blog\/category\/devops\/\" title=\"DevOps\">DevOps<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tAula 16: Functions e Stored Procedures em PL\/pgSQL \u2014 Domine a L\u00f3gica no Banco de Dados\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/jrtx.com.br\/blog"},{"label":"DevOps","link":"https:\/\/jrtx.com.br\/blog\/category\/devops\/"},{"label":"Aula 16: Functions e Stored Procedures em PL\/pgSQL \u2014 Domine a L\u00f3gica no Banco de Dados","link":"https:\/\/jrtx.com.br\/blog\/2026\/07\/10\/aula-16-functions-e-stored-procedures-em-plpgsql-domine-a-lo\/"}],"_links":{"self":[{"href":"https:\/\/jrtx.com.br\/blog\/wp-json\/wp\/v2\/posts\/1464","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jrtx.com.br\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jrtx.com.br\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jrtx.com.br\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jrtx.com.br\/blog\/wp-json\/wp\/v2\/comments?post=1464"}],"version-history":[{"count":0,"href":"https:\/\/jrtx.com.br\/blog\/wp-json\/wp\/v2\/posts\/1464\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/jrtx.com.br\/blog\/wp-json\/wp\/v2\/media\/1463"}],"wp:attachment":[{"href":"https:\/\/jrtx.com.br\/blog\/wp-json\/wp\/v2\/media?parent=1464"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jrtx.com.br\/blog\/wp-json\/wp\/v2\/categories?post=1464"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jrtx.com.br\/blog\/wp-json\/wp\/v2\/tags?post=1464"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}