{"id":1683,"date":"2026-07-20T18:21:36","date_gmt":"2026-07-20T21:21:36","guid":{"rendered":"https:\/\/jrtx.com.br\/blog\/2026\/07\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/"},"modified":"2026-07-20T18:21:36","modified_gmt":"2026-07-20T21:21:36","slug":"aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci","status":"publish","type":"post","link":"https:\/\/jrtx.com.br\/blog\/2026\/07\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/","title":{"rendered":"Aula 17: Triggers no PostgreSQL \u2014 automa\u00e7\u00e3o de regras de neg\u00f3cio"},"content":{"rendered":"<p>Triggers no PostgreSQL representam um dos mecanismos mais poderosos para automatizar regras de neg\u00f3cio diretamente no banco de dados. Em vez de depender exclusivamente da camada de aplica\u00e7\u00e3o para validar, auditar ou transformar dados, os <strong>triggers<\/strong> permitem que voc\u00ea defina comportamentos autom\u00e1ticos que s\u00e3o disparados sempre que determinados eventos ocorrem em uma tabela \u2014 como inser\u00e7\u00f5es, atualiza\u00e7\u00f5es ou exclus\u00f5es. Esta aula \u00e9 um mergulho profundo nesse recurso, cobrindo desde a sintaxe fundamental at\u00e9 cen\u00e1rios avan\u00e7ados de uso em produ\u00e7\u00e3o.<\/p>\n<p>Ao longo dos \u00faltimos anos, em nossos projetos na <strong>JRT Technology Solutions<\/strong>, implementamos centenas de triggers para resolver problemas que iam desde simples auditorias de altera\u00e7\u00f5es at\u00e9 complexas valida\u00e7\u00f5es cruzadas entre tabelas. O que aprendemos na pr\u00e1tica \u00e9 que dominar <strong>Triggers no PostgreSQL<\/strong> n\u00e3o \u00e9 apenas saber escrever a fun\u00e7\u00e3o gatilho \u2014 \u00e9 entender o ciclo de vida da execu\u00e7\u00e3o, as vari\u00e1veis de contexto como <strong>NEW<\/strong> e <strong>OLD<\/strong>, as diferen\u00e7as entre triggers <strong>BEFORE<\/strong> e <strong>AFTER<\/strong>, e principalmente como depurar e otimizar essas estruturas para que n\u00e3o se tornem gargalos de performance.<\/p>\n<p>Nesta aula, voc\u00ea vai colocar a m\u00e3o no c\u00f3digo. Vamos criar triggers reais, test\u00e1-los em um ambiente controlado, verificar o comportamento com consultas de diagn\u00f3stico e ainda analisar os erros mais comuns que profissionais enfrentam no dia a dia. Se voc\u00ea j\u00e1 concluiu as aulas anteriores do curso, j\u00e1 possui uma base s\u00f3lida em fun\u00e7\u00f5es PL\/pgSQL, estrutura de tabelas e \u00edndices \u2014 agora \u00e9 o momento de levar essa automa\u00e7\u00e3o para o pr\u00f3ximo n\u00edvel, incorporando l\u00f3gica reativa diretamente ao seu modelo de dados.<\/p>\n<p>Ao final desta aula, voc\u00ea ter\u00e1 plena capacidade de: projetar triggers que refor\u00e7am a integridade dos dados sem depender de c\u00f3digo externo; auditar automaticamente altera\u00e7\u00f5es em tabelas cr\u00edticas; impedir opera\u00e7\u00f5es inv\u00e1lidas com mensagens de erro personalizadas; e monitorar a execu\u00e7\u00e3o dos seus triggers usando as vis\u00f5es de cat\u00e1logo do PostgreSQL. Tudo isso seguindo um passo a passo detalhado, com cada comando explicado linha por linha, em um formato que voc\u00ea pode replicar imediatamente no seu ambiente.<\/p>\n<h3>O que voc\u00ea vai aprender nesta aula<\/h3>\n<ul>\n<li>Compreender o conceito de <strong>Triggers no PostgreSQL<\/strong> e sua fun\u00e7\u00e3o na automa\u00e7\u00e3o de regras de neg\u00f3cio<\/li>\n<li>Diferenciar triggers <strong>BEFORE<\/strong>, <strong>AFTER<\/strong> e <strong>INSTEAD OF<\/strong>, entendendo quando utilizar cada tipo<\/li>\n<li>Criar fun\u00e7\u00f5es gatilho em PL\/pgSQL que manipulam as vari\u00e1veis <strong>NEW<\/strong> e <strong>OLD<\/strong><\/li>\n<li>Implementar triggers para auditoria (log de altera\u00e7\u00f5es), valida\u00e7\u00e3o de dados e manuten\u00e7\u00e3o de colunas derivadas<\/li>\n<li>Configurar triggers condicionais com a cl\u00e1usula <strong>WHEN<\/strong> para filtrar eventos espec\u00edficos<\/li>\n<li>Gerenciar triggers \u2014 listar, desabilitar, habilitar e remov\u00ea-los com comandos do cat\u00e1logo<\/li>\n<li>Diagnosticar e corrigir os erros mais comuns usando as vis\u00f5es <strong>pg_trigger<\/strong> e <strong>information_schema.triggers<\/strong><\/li>\n<li>Aplicar boas pr\u00e1ticas de performance e organiza\u00e7\u00e3o de c\u00f3digo em ambientes de produ\u00e7\u00e3o<\/li>\n<\/ul>\n<h3>Pr\u00e9-requisitos e Ambiente<\/h3>\n<p>Para executar todos os exemplos desta aula sem interrup\u00e7\u00f5es, voc\u00ea precisa ter um servidor PostgreSQL em funcionamento. As vers\u00f5es testadas e compat\u00edveis s\u00e3o a <strong>PostgreSQL 14<\/strong>, <strong>15<\/strong> e <strong>16<\/strong> \u2014 as tr\u00eas vers\u00f5es est\u00e1veis mais recentes at\u00e9 julho de 2026. O banco de dados que utilizaremos se chama <strong>db_triggers_demo<\/strong>, e vamos cri\u00e1-lo do zero durante a aula. Voc\u00ea precisar\u00e1 de um usu\u00e1rio com privil\u00e9gios de <strong>CREATEDB<\/strong> (ou o superusu\u00e1rio <strong>postgres<\/strong>) para criar o banco e, dentro dele, permiss\u00f5es para criar fun\u00e7\u00f5es e triggers. Se voc\u00ea seguiu as aulas anteriores do curso, j\u00e1 possui o PostgreSQL instalado e configurado; caso contr\u00e1rio, revise a <strong>Aula 1<\/strong> (instala\u00e7\u00e3o no Ubuntu\/Debian e CentOS\/RHEL\/Rocky Linux) e a <strong>Aula 14<\/strong> (fun\u00e7\u00f5es PL\/pgSQL).<\/p>\n<p>Todo o c\u00f3digo desta aula foi projetado para ser executado no <strong>psql<\/strong>, o cliente de linha de comando do PostgreSQL, pois ele oferece feedback imediato e \u00e9 o ambiente onde a maioria dos DBAs trabalha. Se voc\u00ea preferir usar uma interface gr\u00e1fica como <strong>pgAdmin<\/strong> ou <strong>DBeaver<\/strong>, os comandos SQL s\u00e3o exatamente os mesmos \u2014 apenas a forma de envio muda. Durante os exemplos, utilizaremos as vari\u00e1veis de ambiente padr\u00e3o do PostgreSQL (<strong>PGHOST<\/strong>, <strong>PGPORT<\/strong>, <strong>PGUSER<\/strong>) ou passaremos as credenciais explicitamente via <strong>psql -U usuario -d banco<\/strong>.<\/p>\n<p>Antes de come\u00e7ar, certifique-se de que a extens\u00e3o <strong>plpgsql<\/strong> est\u00e1 dispon\u00edvel. Ela vem habilitada por padr\u00e3o em todas as instala\u00e7\u00f5es modernas do PostgreSQL, mas vamos verific\u00e1-la explicitamente. Abra um terminal e execute os comandos iniciais de prepara\u00e7\u00e3o que mostraremos na pr\u00f3xima se\u00e7\u00e3o. O ambiente est\u00e1 pronto? Ent\u00e3o vamos mergulhar nos fundamentos te\u00f3ricos que sustentam o funcionamento dos triggers.<\/p>\n<h3>Fundamentos Te\u00f3ricos: O que s\u00e3o Triggers no PostgreSQL e como funcionam<\/h3>\n<p>Um trigger no PostgreSQL \u00e9 um mecanismo que associa uma fun\u00e7\u00e3o previamente definida a um evento espec\u00edfico em uma tabela ou vis\u00e3o. Quando o evento ocorre \u2014 por exemplo, uma linha \u00e9 inserida na tabela <strong>clientes<\/strong> \u2014 o PostgreSQL automaticamente executa a fun\u00e7\u00e3o associada, passando informa\u00e7\u00f5es de contexto sobre a opera\u00e7\u00e3o. Essa arquitetura em duas partes (fun\u00e7\u00e3o + trigger) \u00e9 uma decis\u00e3o de design importante: a fun\u00e7\u00e3o cont\u00e9m a l\u00f3gica, e o trigger define <em>quando<\/em> e <em>em quais condi\u00e7\u00f5es<\/em> essa l\u00f3gica ser\u00e1 disparada. Isso permite reutilizar a mesma fun\u00e7\u00e3o em m\u00faltiplos triggers e facilita a manuten\u00e7\u00e3o do c\u00f3digo.<\/p>\n<p>Existem tr\u00eas momentos principais em que um trigger pode ser disparado: <strong>BEFORE<\/strong> (antes da opera\u00e7\u00e3o ser conclu\u00edda), <strong>AFTER<\/strong> (ap\u00f3s a opera\u00e7\u00e3o ser conclu\u00edda) e <strong>INSTEAD OF<\/strong> (no lugar da opera\u00e7\u00e3o, exclusivo para vis\u00f5es). Um trigger <strong>BEFORE<\/strong> \u00e9 ideal para validar ou modificar os dados que est\u00e3o prestes a ser gravados \u2014 voc\u00ea pode alterar o valor de colunas na vari\u00e1vel <strong>NEW<\/strong> antes que cheguem ao disco. J\u00e1 um trigger <strong>AFTER<\/strong> \u00e9 perfeito para tarefas de auditoria, replica\u00e7\u00e3o ou atualiza\u00e7\u00e3o de cache, pois nesse momento a opera\u00e7\u00e3o j\u00e1 foi confirmada e voc\u00ea tem acesso tanto aos valores antigos (<strong>OLD<\/strong>) quanto aos novos (<strong>NEW<\/strong>). Triggers <strong>INSTEAD OF<\/strong> s\u00e3o usados em vis\u00f5es complexas onde o PostgreSQL n\u00e3o consegue determinar automaticamente como traduzir uma opera\u00e7\u00e3o DML em altera\u00e7\u00f5es nas tabelas subjacentes.<\/p>\n<p>Um aspecto frequentemente subestimado \u00e9 o entendimento das vari\u00e1veis de registro <strong>NEW<\/strong> e <strong>OLD<\/strong>. Em um trigger de <strong>INSERT<\/strong>, apenas <strong>NEW<\/strong> est\u00e1 dispon\u00edvel (contendo a linha que ser\u00e1 inserida). Em <strong>DELETE<\/strong>, apenas <strong>OLD<\/strong> est\u00e1 dispon\u00edvel (contendo a linha que ser\u00e1 removida). Em <strong>UPDATE<\/strong>, ambas est\u00e3o dispon\u00edveis: <strong>OLD<\/strong> cont\u00e9m os valores antes da modifica\u00e7\u00e3o, e <strong>NEW<\/strong> cont\u00e9m os valores ap\u00f3s a modifica\u00e7\u00e3o. Essa distin\u00e7\u00e3o \u00e9 crucial: se voc\u00ea tentar acessar <strong>OLD<\/strong> em um trigger de INSERT, receber\u00e1 um erro de vari\u00e1vel n\u00e3o inicializada. Nossos especialistas utilizam diariamente essa distin\u00e7\u00e3o para construir l\u00f3gicas condicionais que reagem de forma diferente conforme o tipo de opera\u00e7\u00e3o.<\/p>\n<p>Outro conceito fundamental \u00e9 o de <strong>gatilho por linha<\/strong> (<em>FOR EACH ROW<\/em>) versus <strong>gatilho por instru\u00e7\u00e3o<\/strong> (<em>FOR EACH STATEMENT<\/em>). Um trigger <strong>FOR EACH ROW<\/strong> \u00e9 executado uma vez para cada linha afetada pela opera\u00e7\u00e3o DML \u2014 se um UPDATE modificar 500 linhas, o trigger dispara 500 vezes. J\u00e1 um trigger <strong>FOR EACH STATEMENT<\/strong> dispara uma \u00fanica vez por instru\u00e7\u00e3o SQL, independentemente do n\u00famero de linhas afetadas. O primeiro \u00e9 o padr\u00e3o e o mais comum para regras de neg\u00f3cio que dependem dos valores individuais de cada linha; o segundo \u00e9 \u00fatil para opera\u00e7\u00f5es de manuten\u00e7\u00e3o globais, como registrar que uma determinada tabela foi modificada. Em ambientes de alto volume, a escolha entre eles tem impacto direto na performance, e vamos discutir isso em detalhes na se\u00e7\u00e3o de boas pr\u00e1ticas.<\/p>\n<table>\n<caption><strong>Tabela 1: Comparativo entre tipos de Triggers no PostgreSQL<\/strong><\/caption>\n<thead>\n<tr>\n<th>Tipo<\/th>\n<th>Momento de Execu\u00e7\u00e3o<\/th>\n<th>Acesso a NEW\/OLD<\/th>\n<th>Uso T\u00edpico<\/th>\n<th>Pode Cancelar Opera\u00e7\u00e3o?<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong>BEFORE<\/strong><\/td>\n<td>Antes da opera\u00e7\u00e3o<\/td>\n<td>NEW (INSERT\/UPDATE), OLD (UPDATE\/DELETE)<\/td>\n<td>Valida\u00e7\u00e3o, normaliza\u00e7\u00e3o, modifica\u00e7\u00e3o de dados<\/td>\n<td>Sim (via RAISE EXCEPTION ou RETURN NULL)<\/td>\n<\/tr>\n<tr>\n<td><strong>AFTER<\/strong><\/td>\n<td>Ap\u00f3s a opera\u00e7\u00e3o<\/td>\n<td>NEW (INSERT\/UPDATE), OLD (UPDATE\/DELETE)<\/td>\n<td>Auditoria, replica\u00e7\u00e3o, notifica\u00e7\u00f5es<\/td>\n<td>N\u00e3o (a opera\u00e7\u00e3o j\u00e1 foi confirmada)<\/td>\n<\/tr>\n<tr>\n<td><strong>INSTEAD OF<\/strong><\/td>\n<td>No lugar da opera\u00e7\u00e3o<\/td>\n<td>NEW (INSERT\/UPDATE), OLD (UPDATE\/DELETE)<\/td>\n<td>Vis\u00f5es complexas, tabelas externas<\/td>\n<td>Sim (substitui completamente a opera\u00e7\u00e3o)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>O PostgreSQL tamb\u00e9m suporta triggers condicionais atrav\u00e9s da cl\u00e1usula <strong>WHEN<\/strong>. Com ela, voc\u00ea pode especificar uma condi\u00e7\u00e3o booleana que filtra exatamente quais linhas disparam o trigger. Por exemplo, voc\u00ea pode auditar apenas altera\u00e7\u00f5es onde o sal\u00e1rio ultrapassa um determinado valor, ou onde uma coluna espec\u00edfica foi modificada (usando <strong>OLD.coluna IS DISTINCT FROM NEW.coluna<\/strong>). Isso reduz drasticamente a sobrecarga em opera\u00e7\u00f5es de grande volume, pois a fun\u00e7\u00e3o gatilho s\u00f3 \u00e9 invocada quando a condi\u00e7\u00e3o \u00e9 satisfeita \u2014 as demais linhas passam diretamente sem qualquer execu\u00e7\u00e3o adicional.<\/p>\n<p>Por fim, \u00e9 essencial entender que triggers operam dentro da mesma transa\u00e7\u00e3o da opera\u00e7\u00e3o que os disparou. Se um trigger <strong>AFTER<\/strong> levantar uma exce\u00e7\u00e3o, toda a transa\u00e7\u00e3o \u00e9 revertida, incluindo a opera\u00e7\u00e3o original. Isso garante a atomicidade das regras de neg\u00f3cio \u2014 ou tudo \u00e9 aplicado consistentemente, ou nada \u00e9 alterado. Essa caracter\u00edstica \u00e9 uma faca de dois gumes: por um lado, protege a integridade; por outro, pode causar rollbacks inesperados se o trigger n\u00e3o for cuidadosamente testado. Na JRT Technology Solutions, sempre recomendamos testes exaustivos em ambiente de homologa\u00e7\u00e3o antes de implantar triggers em produ\u00e7\u00e3o.<\/p>\n<h3>Criando o Ambiente de Testes: Banco, Tabelas e Dados Iniciais<\/h3>\n<p>Vamos construir nosso laborat\u00f3rio do zero. O primeiro passo \u00e9 criar o banco de dados que utilizaremos em todos os exemplos desta aula. Conecte-se ao PostgreSQL com um usu\u00e1rio que tenha privil\u00e9gios de cria\u00e7\u00e3o de banco (ou use o superusu\u00e1rio <strong>postgres<\/strong>) e execute os comandos a seguir. Cada linha est\u00e1 comentada para que voc\u00ea entenda exatamente o que est\u00e1 acontecendo.<\/p>\n<pre><code>-- 1. Cria\u00e7\u00e3o do banco de dados db_triggers_demo\n-- O encoding UTF8 garante suporte a caracteres acentuados (portugu\u00eas)\n-- LC_COLLATE e LC_CTYPE 'pt_BR.UTF-8' configuram ordena\u00e7\u00e3o para portugu\u00eas do Brasil\n-- Se seu sistema n\u00e3o tiver locale pt_BR dispon\u00edvel, use 'en_US.UTF-8' ou 'C'\nCREATE DATABASE db_triggers_demo\n    ENCODING 'UTF8'\n    LC_COLLATE 'pt_BR.UTF-8'\n    LC_CTYPE 'pt_BR.UTF-8'\n    TEMPLATE template0;\n\n-- 2. Conectar ao novo banco\n-- No psql: \\c db_triggers_demo\n-- Ou pela linha de comando: psql -U postgres -d db_triggers_demo<\/code><\/pre>\n<p>Ap\u00f3s criar o banco, conecte-se a ele e vamos criar as tabelas que servir\u00e3o de base para os nossos triggers. Teremos uma tabela principal chamada <strong>funcionarios<\/strong> (que ser\u00e1 o alvo dos nossos triggers) e uma tabela auxiliar <strong>audit_funcionarios<\/strong> (que armazenar\u00e1 o hist\u00f3rico de altera\u00e7\u00f5es). Al\u00e9m disso, criaremos uma tabela <strong>departamentos<\/strong> para demonstrar triggers com valida\u00e7\u00e3o entre tabelas.<\/p>\n<pre><code>-- 3. Cria\u00e7\u00e3o da tabela departamentos (tabela de refer\u00eancia)\nCREATE TABLE departamentos (\n    id_depto        SERIAL PRIMARY KEY,\n    nome_depto      VARCHAR(100) NOT NULL UNIQUE,\n    orcamento       NUMERIC(12,2) NOT NULL CHECK (orcamento > 0),\n    data_criacao    TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP\n);\n\n-- 4. Inser\u00e7\u00e3o de dados iniciais nos departamentos\nINSERT INTO departamentos (nome_depto, orcamento) VALUES\n    ('Tecnologia', 1500000.00),\n    ('Financeiro', 800000.00),\n    ('Recursos Humanos', 450000.00),\n    ('Marketing', 620000.00),\n    ('Opera\u00e7\u00f5es', 1100000.00);\n\n-- 5. Cria\u00e7\u00e3o da tabela funcionarios (tabela principal)\nCREATE TABLE funcionarios (\n    id_func         SERIAL PRIMARY KEY,\n    nome            VARCHAR(150) NOT NULL,\n    email           VARCHAR(200) UNIQUE,\n    salario         NUMERIC(10,2) NOT NULL CHECK (salario >= 1412.00),\n    bonus           NUMERIC(10,2) DEFAULT 0.00,\n    salario_total   NUMERIC(10,2) GENERATED ALWAYS AS (salario + bonus) STORED,\n    id_depto        INTEGER REFERENCES departamentos(id_depto) ON DELETE SET NULL,\n    data_admissao   DATE DEFAULT CURRENT_DATE,\n    ativo           BOOLEAN DEFAULT TRUE,\n    ultima_modif    TIMESTAMPTZ\n);\n\n-- 6. Cria\u00e7\u00e3o da tabela de auditoria (armazenar\u00e1 hist\u00f3rico de altera\u00e7\u00f5es)\nCREATE TABLE audit_funcionarios (\n    id_audit        SERIAL PRIMARY KEY,\n    operacao        CHAR(6) NOT NULL,          -- 'INSERT', 'UPDATE', 'DELETE'\n    id_func         INTEGER,\n    nome_old        VARCHAR(150),\n    nome_new        VARCHAR(150),\n    salario_old     NUMERIC(10,2),\n    salario_new     NUMERIC(10,2),\n    id_depto_old    INTEGER,\n    id_depto_new    INTEGER,\n    usuario_bd      VARCHAR(100),              -- CURRENT_USER\n    data_operacao   TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP\n);\n\n-- 7. Inser\u00e7\u00e3o de funcion\u00e1rios iniciais\nINSERT INTO funcionarios (nome, email, salario, id_depto) VALUES\n    ('Ana Silva', 'ana.silva@empresa.com', 8500.00, 1),\n    ('Bruno Costa', 'bruno.costa@empresa.com', 6200.00, 2),\n    ('Carla Mendes', 'carla.mendes@empresa.com', 7200.00, 1),\n    ('Daniel Oliveira', 'daniel.oliveira@empresa.com', 4800.00, 3),\n    ('Elena Santos', 'elena.santos@empresa.com', 9100.00, 4);<\/code><\/pre>\n<p>Agora temos um ambiente completo para trabalhar. A tabela <strong>funcionarios<\/strong> possui uma coluna gerada automaticamente (<strong>salario_total<\/strong>) e uma coluna <strong>ultima_modif<\/strong> que ainda n\u00e3o est\u00e1 sendo atualizada automaticamente \u2014 perfeita para demonstrar nosso primeiro trigger. A tabela <strong>audit_funcionarios<\/strong> est\u00e1 vazia, pronta para receber os registros de auditoria que nossos triggers v\u00e3o gerar. Nos pr\u00f3ximos passos, voc\u00ea ver\u00e1 como fazer tudo isso funcionar de forma integrada.<\/p>\n<p>Vamos executar uma consulta r\u00e1pida para verificar se os dados foram inseridos corretamente e se a coluna gerada <strong>salario_total<\/strong> est\u00e1 funcionando como esperado (ela deve refletir <strong>salario + bonus<\/strong>, atualmente com bonus zerado).<\/p>\n<pre><code>-- 8. Verifica\u00e7\u00e3o dos dados iniciais\nSELECT id_func, nome, salario, bonus, salario_total, nome_depto\nFROM funcionarios f\nLEFT JOIN departamentos d ON f.id_depto = d.id_depto\nORDER BY id_func;<\/code><\/pre>\n<pre><code class=\"output\"> id_func |       nome        | salario | bonus | salario_total |    nome_depto    \n---------+-------------------+---------+-------+---------------+------------------\n       1 | Ana Silva         | 8500.00 |  0.00 |       8500.00 | Tecnologia\n       2 | Bruno Costa       | 6200.00 |  0.00 |       6200.00 | Financeiro\n       3 | Carla Mendes      | 7200.00 |  0.00 |       7200.00 | Tecnologia\n       4 | Daniel Oliveira   | 4800.00 |  0.00 |       4800.00 | Recursos Humanos\n       5 | Elena Santos      | 9100.00 |  0.00 |       9100.00 | Marketing\n(5 rows)<\/code><\/pre>\n<h3>Primeiro Trigger: Atualiza\u00e7\u00e3o Autom\u00e1tica de Timestamp<\/h3>\n<p>Nosso primeiro trigger ser\u00e1 um cl\u00e1ssico da administra\u00e7\u00e3o de bancos de dados: atualizar automaticamente a coluna <strong>ultima_modif<\/strong> sempre que uma linha da tabela <strong>funcionarios<\/strong> for modificada. Embora pare\u00e7a simples, este caso de uso introduz a estrutura b\u00e1sica que todo trigger no PostgreSQL segue: uma fun\u00e7\u00e3o gatilho em PL\/pgSQL e o comando <strong>CREATE TRIGGER<\/strong> que a vincula \u00e0 tabela. Vamos criar a fun\u00e7\u00e3o primeiro, depois o trigger, e em seguida testar com opera\u00e7\u00f5es reais de UPDATE.<\/p>\n<p>A fun\u00e7\u00e3o gatilho tem uma assinatura especial: ela deve retornar o tipo <strong>TRIGGER<\/strong> (n\u00e3o <strong>VOID<\/strong>, n\u00e3o <strong>INTEGER<\/strong> \u2014 exclusivamente <strong>TRIGGER<\/strong>). Dentro dela, utilizamos as vari\u00e1veis especiais <strong>NEW<\/strong> (registro que cont\u00e9m a nova linha) e <strong>TG_OP<\/strong> (uma string que indica a opera\u00e7\u00e3o: &#8216;INSERT&#8217;, &#8216;UPDATE&#8217;, &#8216;DELETE&#8217; ou &#8216;TRUNCATE&#8217;). Para nossa finalidade, sempre que a opera\u00e7\u00e3o for UPDATE, vamos atualizar <strong>NEW.ultima_modif<\/strong> com o timestamp atual. Para INSERT, tamb\u00e9m vamos definir o timestamp, embora a coluna j\u00e1 possa ter um valor padr\u00e3o \u2014 esta \u00e9 uma conven\u00e7\u00e3o que muitos DBAs adotam para consist\u00eancia.<\/p>\n<pre><code>-- 9. Fun\u00e7\u00e3o gatilho para atualizar ultima_modif automaticamente\n-- A linguagem \u00e9 PL\/pgSQL e o tipo de retorno \u00e9 TRIGGER (obrigat\u00f3rio)\nCREATE OR REPLACE FUNCTION fn_atualiza_timestamp_modificacao()\nRETURNS TRIGGER\nLANGUAGE plpgsql\nAS $$\nBEGIN\n    -- Em qualquer opera\u00e7\u00e3o INSERT ou UPDATE, atualizamos o timestamp\n    -- TG_OP \u00e9 uma vari\u00e1vel autom\u00e1tica do PostgreSQL que indica a opera\u00e7\u00e3o\n    IF TG_OP IN ('INSERT', 'UPDATE') THEN\n        NEW.ultima_modif := CURRENT_TIMESTAMP;\n    END IF;\n    \n    -- Para triggers BEFORE INSERT\/UPDATE, devemos retornar NEW\n    -- Se retornarmos NULL, a opera\u00e7\u00e3o \u00e9 cancelada silenciosamente\n    RETURN NEW;\nEND;\n$$;\n\n-- 10. Cria\u00e7\u00e3o do trigger propriamente dito\n-- Ele executar\u00e1 a fun\u00e7\u00e3o fn_atualiza_timestamp_modificacao ANTES de cada INSERT ou UPDATE\nCREATE TRIGGER trg_atualiza_timestamp\n    BEFORE INSERT OR UPDATE\n    ON funcionarios\n    FOR EACH ROW\n    EXECUTE FUNCTION fn_atualiza_timestamp_modificacao();<\/code><\/pre>\n<p>Vamos entender cada parte do comando <strong>CREATE TRIGGER<\/strong>. A cl\u00e1usula <strong>BEFORE INSERT OR UPDATE<\/strong> define que o trigger ser\u00e1 disparado antes que a opera\u00e7\u00e3o de inser\u00e7\u00e3o ou atualiza\u00e7\u00e3o seja concretizada \u2014 isso \u00e9 essencial porque queremos modificar <strong>NEW.ultima_modif<\/strong> antes que o PostgreSQL grave a linha no disco. A especifica\u00e7\u00e3o <strong>ON funcionarios<\/strong> vincula o trigger a essa tabela. <strong>FOR EACH ROW<\/strong> indica que a fun\u00e7\u00e3o ser\u00e1 executada uma vez para cada linha afetada. Por fim, <strong>EXECUTE FUNCTION<\/strong> (que a partir do PostgreSQL 11 substituiu o antigo <strong>EXECUTE PROCEDURE<\/strong>, embora este \u00faltimo ainda funcione por compatibilidade) associa a fun\u00e7\u00e3o gatilho que criamos.<\/p>\n<p>Agora vamos testar nosso trigger realizando uma atualiza\u00e7\u00e3o em um dos funcion\u00e1rios e verificando se a coluna <strong>ultima_modif<\/strong> foi preenchida automaticamente.<\/p>\n<pre><code>-- 11. Teste: atualizar o sal\u00e1rio da Ana Silva\nUPDATE funcionarios\nSET salario = 9200.00\nWHERE id_func = 1;\n\n-- 12. Verifica\u00e7\u00e3o do resultado\n-- A coluna ultima_modif deve conter o timestamp do exato momento do UPDATE\nSELECT id_func, nome, salario, bonus, salario_total, ultima_modif\nFROM funcionarios\nWHERE id_func = 1;<\/code><\/pre>\n<pre><code class=\"output\"> id_func |   nome    | salario | bonus | salario_total |         ultima_modif         \n---------+-----------+---------+-------+---------------+-------------------------------\n       1 | Ana Silva | 9200.00 |  0.00 |       9200.00 | 2026-07-20 14:32:17.456789-03\n(1 row)<\/code><\/pre>\n<p>Perfeito! O trigger funcionou exatamente como esperado. A coluna <strong>ultima_modif<\/strong> foi preenchida automaticamente com o timestamp do momento em que o UPDATE foi executado. Note que n\u00e3o precisamos incluir essa coluna no comando UPDATE \u2014 o trigger cuidou disso de forma transparente para a aplica\u00e7\u00e3o. Esse \u00e9 um exemplo cl\u00e1ssico de como <strong>Triggers no PostgreSQL<\/strong> podem simplificar a camada de aplica\u00e7\u00e3o e garantir consist\u00eancia nos metadados das tabelas.<\/p>\n<p>Agora vamos testar tamb\u00e9m a opera\u00e7\u00e3o de INSERT para confirmar que o trigger tamb\u00e9m atua nesse caso:<\/p>\n<pre><code>-- 13. Teste de INSERT com o trigger\nINSERT INTO funcionarios (nome, email, salario, id_depto)\nVALUES ('Fernando Lima', 'fernando.lima@empresa.com', 5500.00, 2);\n\n-- 14. Verifica\u00e7\u00e3o\nSELECT id_func, nome, salario, ultima_modif\nFROM funcionarios\nWHERE nome = 'Fernando Lima';<\/code><\/pre>\n<pre><code class=\"output\"> id_func |     nome      | salario |         ultima_modif         \n---------+---------------+---------+-------------------------------\n       6 | Fernando Lima | 5500.00 | 2026-07-20 14:33:42.123456-03\n(1 row)<\/code><\/pre>\n<h3>Trigger de Auditoria: Registrando Todas as Altera\u00e7\u00f5es em uma Tabela de Hist\u00f3rico<\/h3>\n<p>A auditoria de dados \u00e9 provavelmente o caso de uso mais comum para <strong>Triggers no PostgreSQL<\/strong> em ambientes corporativos. A ideia \u00e9 simples: sempre que uma linha for inserida, modificada ou exclu\u00edda na tabela principal, uma linha correspondente \u00e9 inserida na tabela de auditoria, registrando quem fez a altera\u00e7\u00e3o, quando, e quais valores foram alterados. Este padr\u00e3o \u00e9 essencial para compliance regulat\u00f3rio (LGPD, SOX, PCI-DSS) e tamb\u00e9m para troubleshooting \u2014 quando uma altera\u00e7\u00e3o indevida \u00e9 detectada, a trilha de auditoria permite identificar rapidamente a origem do problema.<\/p>\n<p>Em nossos projetos na JRT Technology Solutions, implementamos auditoria baseada em triggers para tabelas que armazenam dados sens\u00edveis como informa\u00e7\u00f5es financeiras, dados pessoais de clientes e configura\u00e7\u00f5es cr\u00edticas de sistema. O padr\u00e3o que vamos construir agora \u00e9 o mesmo que utilizamos em produ\u00e7\u00e3o, com algumas simplifica\u00e7\u00f5es did\u00e1ticas. A fun\u00e7\u00e3o gatilho precisar\u00e1 diferenciar entre INSERT, UPDATE e DELETE para registrar as informa\u00e7\u00f5es corretas na tabela de auditoria.<\/p>\n<p>Criaremos uma segunda fun\u00e7\u00e3o gatilho e um segundo trigger na tabela <strong>funcionarios<\/strong>. Como o PostgreSQL permite m\u00faltiplos triggers na mesma tabela (desde que tenham nomes diferentes), ambos coexistir\u00e3o sem conflitos. A ordem de execu\u00e7\u00e3o entre triggers do mesmo tipo (BEFORE, AFTER) \u00e9 alfab\u00e9tica por nome, mas para triggers de tipos diferentes, a preced\u00eancia natural \u00e9: BEFORE \u2192 opera\u00e7\u00e3o da tabela \u2192 AFTER. Nosso trigger de auditoria ser\u00e1 do tipo <strong>AFTER<\/strong>, garantindo que a opera\u00e7\u00e3o j\u00e1 foi confirmada antes de registrarmos o log.<\/p>\n<pre><code>-- 15. Fun\u00e7\u00e3o gatilho para auditoria completa (INSERT, UPDATE, DELETE)\nCREATE OR REPLACE FUNCTION fn_audita_funcionarios()\nRETURNS TRIGGER\nLANGUAGE plpgsql\nAS $$\nBEGIN\n    -- Tratamento para INSERT: OLD n\u00e3o est\u00e1 dispon\u00edvel, apenas NEW\n    IF TG_OP = 'INSERT' THEN\n        INSERT INTO audit_funcionarios (operacao, id_func, nome_new, salario_new, id_depto_new, usuario_bd)\n        VALUES ('INSERT', NEW.id_func, NEW.nome, NEW.salario, NEW.id_depto, CURRENT_USER);\n        RETURN NEW;  -- Em AFTER triggers, o retorno \u00e9 ignorado, mas \u00e9 boa pr\u00e1tica retornar\n    \n    -- Tratamento para UPDATE: OLD e NEW est\u00e3o dispon\u00edveis\n    ELSIF TG_OP = 'UPDATE' THEN\n        INSERT INTO audit_funcionarios (operacao, id_func,\n                                        nome_old, nome_new,\n                                        salario_old, salario_new,\n                                        id_depto_old, id_depto_new,\n                                        usuario_bd)\n        VALUES ('UPDATE', NEW.id_func,\n                OLD.nome, NEW.nome,\n                OLD.salario, NEW.salario,\n                OLD.id_depto, NEW.id_depto,\n                CURRENT_USER);\n        RETURN NEW;\n    \n    -- Tratamento para DELETE: NEW n\u00e3o est\u00e1 dispon\u00edvel, apenas OLD\n    ELSIF TG_OP = 'DELETE' THEN\n        INSERT INTO audit_funcionarios (operacao, id_func, nome_old, salario_old, id_depto_old, usuario_bd)\n        VALUES ('DELETE', OLD.id_func, OLD.nome, OLD.salario, OLD.id_depto, CURRENT_USER);\n        RETURN OLD;\n    END IF;\n    \n    -- Fallback de seguran\u00e7a (nunca deve ser atingido)\n    RETURN NULL;\nEND;\n$$;\n\n-- 16. Cria\u00e7\u00e3o do trigger de auditoria (AFTER INSERT, UPDATE, DELETE)\nCREATE TRIGGER trg_audita_funcionarios\n    AFTER INSERT OR UPDATE OR DELETE\n    ON funcionarios\n    FOR EACH ROW\n    EXECUTE FUNCTION fn_audita_funcionarios();<\/code><\/pre>\n<p>Note a diferen\u00e7a crucial: usamos <strong>AFTER<\/strong> em vez de <strong>BEFORE<\/strong>. Isso porque n\u00e3o queremos modificar os dados \u2014 apenas registrar o que aconteceu. Se tent\u00e1ssemos fazer isso com um trigger <strong>BEFORE<\/strong> e a opera\u00e7\u00e3o fosse posteriormente revertida por uma constraint violation, registrar\u00edamos uma auditoria de algo que nunca aconteceu. O trigger <strong>AFTER<\/strong> garante que s\u00f3 registramos opera\u00e7\u00f5es que foram efetivamente persistidas. Al\u00e9m disso, observe a distin\u00e7\u00e3o entre os tr\u00eas blocos condicionais: INSERT s\u00f3 tem NEW, DELETE s\u00f3 tem OLD, e UPDATE tem ambos. Tentar acessar OLD em um INSERT resultaria em erro.<\/p>\n<p>Vamos testar o cen\u00e1rio completo: fazer um INSERT, um UPDATE e um DELETE, e depois consultar a tabela de auditoria para ver o resultado.<\/p>\n<pre><code>-- 17. Inserir um novo funcion\u00e1rio\nINSERT INTO funcionarios (nome, email, salario, id_depto)\nVALUES ('Gabriela Rocha', 'gabriela.rocha@empresa.com', 6800.00, 1);\n\n-- 18. Atualizar o sal\u00e1rio do Bruno Costa\nUPDATE funcionarios\nSET salario = 7000.00\nWHERE id_func = 2;\n\n-- 19. Remover o Daniel Oliveira\nDELETE FROM funcionarios\nWHERE id_func = 4;\n\n-- 20. Consultar a tabela de auditoria para ver todas as opera\u00e7\u00f5es registradas\nSELECT id_audit, operacao, id_func, \n       nome_old, nome_new, \n       salario_old, salario_new,\n       usuario_bd,\n       data_operacao\nFROM audit_funcionarios\nORDER BY id_audit;<\/code><\/pre>\n<pre><code class=\"output\"> id_audit | operacao | id_func |    nome_old     |    nome_new      | salario_old | salario_new | usuario_bd |        data_operacao         \n----------+----------+---------+-----------------+------------------+-------------+-------------+------------+-------------------------------\n        1 | INSERT   |       7 |                 | Gabriela Rocha   |             |     6800.00 | postgres   | 2026-07-20 14:38:55.123456-03\n        2 | UPDATE   |       2 | Bruno Costa     | Bruno Costa      |     6200.00 |     7000.00 | postgres   | 2026-07-20 14:39:20.654321-03\n        3 | DELETE   |       4 | Daniel Oliveira |                  |     4800.00 |             | postgres   | 2026-07-20 14:39:45.789012-03\n(3 rows)<\/code><\/pre>\n<p>A tabela de auditoria agora cont\u00e9m um registro detalhado de cada opera\u00e7\u00e3o. Na linha 1, vemos o INSERT de Gabriela Rocha \u2014 as colunas <strong>nome_old<\/strong> e <strong>salario_old<\/strong> est\u00e3o nulas porque, em um INSERT, n\u00e3o h\u00e1 valores antigos. Na linha 2, o UPDATE de Bruno Costa mostra o sal\u00e1rio antes (6200.00) e depois (7000.00), com o nome mantido igual. Na linha 3, o DELETE de Daniel Oliveira mostra apenas os valores antigos, com as colunas <strong>_new<\/strong> nulas. O campo <strong>usuario_bd<\/strong> registra o usu\u00e1rio do PostgreSQL que executou a opera\u00e7\u00e3o \u2014 em um ambiente com m\u00faltiplos usu\u00e1rios de aplica\u00e7\u00e3o, essa informa\u00e7\u00e3o \u00e9 crucial para rastreabilidade.<\/p>\n<table>\n<caption><strong>Tabela 2: Vari\u00e1veis especiais dispon\u00edveis em fun\u00e7\u00f5es gatilho (PL\/pgSQL)<\/strong><\/caption>\n<thead>\n<tr>\n<th>Vari\u00e1vel<\/th>\n<th>Tipo<\/th>\n<th>Descri\u00e7\u00e3o<\/th>\n<th>Dispon\u00edvel em<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><strong>NEW<\/strong><\/td>\n<td>RECORD<\/td>\n<td>Novo registro (linha ap\u00f3s a opera\u00e7\u00e3o)<\/td>\n<td>INSERT, UPDATE<\/td>\n<\/tr>\n<tr>\n<td><strong>OLD<\/strong><\/td>\n<td>RECORD<\/td>\n<td>Registro antigo (linha antes da opera\u00e7\u00e3o)<\/td>\n<td>UPDATE, DELETE<\/td>\n<\/tr>\n<tr>\n<td><strong>TG_OP<\/strong><\/td>\n<td>text<\/td>\n<td>Tipo de opera\u00e7\u00e3o: &#8216;INSERT&#8217;, &#8216;UPDATE&#8217;, &#8216;DELETE&#8217;, &#8216;TRUNCATE&#8217;<\/td>\n<td>Todos<\/td>\n<\/tr>\n<tr>\n<td><strong>TG_NAME<\/strong><\/td>\n<td>name<\/td>\n<td>Nome do trigger que est\u00e1 executando<\/td>\n<td>Todos<\/td>\n<\/tr>\n<tr>\n<td><strong>TG_TABLE_NAME<\/strong><\/td>\n<td>name<\/td>\n<td>Nome da tabela onde o trigger foi disparado<\/td>\n<td>Todos<\/td>\n<\/tr>\n<tr>\n<td><strong>TG_TABLE_SCHEMA<\/strong><\/td>\n<td>name<\/td>\n<td>Esquema da tabela onde o trigger foi disparado<\/td>\n<td>Todos<\/td>\n<\/tr>\n<tr>\n<td><strong>TG_WHEN<\/strong><\/td>\n<td>text<\/td>\n<td>&#8216;BEFORE&#8217;, &#8216;AFTER&#8217; ou &#8216;INSTEAD OF&#8217;<\/td>\n<td>Todos<\/td>\n<\/tr>\n<tr>\n<td><strong>TG_LEVEL<\/strong><\/td>\n<td>text<\/td>\n<td>&#8216;ROW&#8217; ou &#8216;STATEMENT&#8217;<\/td>\n<td>Todos<\/td>\n<\/tr>\n<tr>\n<td><strong>TG_ARGV[]<\/strong><\/td>\n<td>text[]<\/td>\n<td>Argumentos passados na cria\u00e7\u00e3o do trigger<\/td>\n<td>Todos<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>Triggers com Valida\u00e7\u00e3o e Regras de Neg\u00f3cio: Impedindo Opera\u00e7\u00f5es Inv\u00e1lidas<\/h3>\n<p>Uma das aplica\u00e7\u00f5es mais poderosas dos <strong>Triggers no PostgreSQL<\/strong> \u00e9 a valida\u00e7\u00e3o de regras de neg\u00f3cio complexas que v\u00e3o al\u00e9m do que <strong>CHECK constraints<\/strong> podem expressar. Enquanto uma CHECK constraint s\u00f3 pode validar valores dentro da pr\u00f3pria linha sendo inserida ou atualizada, um trigger pode consultar outras tabelas, comparar valores entre a vers\u00e3o antiga e a nova da linha, e at\u00e9 mesmo realizar c\u00e1lculos condicionais antes de decidir se a opera\u00e7\u00e3o deve prosseguir. Vamos construir um exemplo pr\u00e1tico: impedir que um funcion\u00e1rio receba um aumento superior a 50% do seu sal\u00e1rio atual em uma \u00fanica atualiza\u00e7\u00e3o, a menos que o departamento tenha or\u00e7amento suficiente.<\/p>\n<p>Essa regra simula um controle comum em departamentos de RH: aumentos muito expressivos precisam de aprova\u00e7\u00e3o adicional e n\u00e3o podem ser aplicados diretamente no sistema. Para implementar essa valida\u00e7\u00e3o, criaremos um trigger <strong>BEFORE UPDATE<\/strong> que compara <strong>NEW.salario<\/strong> com <strong>OLD.salario<\/strong> e, se o aumento ultrapassar 50%, levanta uma exce\u00e7\u00e3o que cancela a opera\u00e7\u00e3o. Adicionalmente, vamos verificar se o or\u00e7amento do departamento \u00e9 suficiente para cobrir o novo sal\u00e1rio \u2014 uma valida\u00e7\u00e3o que envolve consulta a outra tabela.<\/p>\n<pre><code>-- 21. Fun\u00e7\u00e3o gatilho para validar aumento m\u00e1ximo de 50%\nCREATE OR REPLACE FUNCTION fn_valida_aumento_salarial()\nRETURNS TRIGGER\nLANGUAGE plpgsql\nAS $$\nDECLARE\n    v_orcamento_depto NUMERIC(12,2);\n    v_salario_total_depto NUMERIC(12,2);\n    v_percentual_aumento NUMERIC(5,2);\nBEGIN\n    -- S\u00f3 validamos se o sal\u00e1rio realmente mudou\n    -- Usamos IS DISTINCT FROM para tratar NULLs corretamente\n    IF NEW.salario IS NOT DISTINCT FROM OLD.salario THEN\n        RETURN NEW; -- Sem altera\u00e7\u00e3o, permite\n    END IF;\n    \n    -- Calcula o percentual de aumento\n    -- Se OLD.salario for 0 (n\u00e3o deveria, mas por seguran\u00e7a), tratamos\n    IF OLD.salario > 0 THEN\n        v_percentual_aumento := ((NEW.salario - OLD.salario) \/ OLD.salario) * 100;\n    ELSE\n        v_percentual_aumento := 100; -- Se sal\u00e1rio antigo \u00e9 0, qualquer aumento \u00e9 100%\n    END IF;\n    \n    -- Regra 1: Aumento n\u00e3o pode exceder 50%\n    IF v_percentual_aumento > 50 THEN\n        RAISE EXCEPTION 'Aumento de %.2f%% excede o limite m\u00e1ximo de 50%% para o funcion\u00e1rio ID % (de R$ %.2f para R$ %.2f)',\n            v_percentual_aumento, NEW.id_func, OLD.salario, NEW.salario;\n    END IF;\n    \n    -- Regra 2: Verificar or\u00e7amento do departamento (se mudou de departamento ou sal\u00e1rio)\n    -- Consulta o or\u00e7amento do departamento de destino\n    SELECT orcamento INTO v_orcamento_depto\n    FROM departamentos\n    WHERE id_depto = NEW.id_depto;\n    \n    -- Se o departamento n\u00e3o existe (id_depto NULL ou inv\u00e1lido), permite\n    -- (a FK j\u00e1 garante integridade, mas id_depto pode ser NULL)\n    IF v_orcamento_depto IS NULL AND NEW.id_depto IS NOT NULL THEN\n        RAISE EXCEPTION 'Departamento ID % n\u00e3o encontrado', NEW.id_depto;\n    END IF;\n    \n    -- Se h\u00e1 or\u00e7amento definido, calcula o total de sal\u00e1rios do departamento\n    IF v_orcamento_depto IS NOT NULL THEN\n        SELECT COALESCE(SUM(salario + bonus), 0) INTO v_salario_total_depto\n        FROM funcionarios\n        WHERE id_depto = NEW.id_depto\n          AND id_func != NEW.id_func; -- Exclui o pr\u00f3prio funcion\u00e1rio da soma\n        \n        -- Adiciona o novo sal\u00e1rio do funcion\u00e1rio atual\n        v_salario_total_depto := v_salario_total_depto + NEW.salario + COALESCE(NEW.bonus, 0);\n        \n        -- Se o total de sal\u00e1rios excede o or\u00e7amento, rejeita\n        IF v_salario_total_depto > v_orcamento_depto THEN\n            RAISE EXCEPTION 'O sal\u00e1rio total do departamento \"%\" (R$ %.2f) excederia o or\u00e7amento dispon\u00edvel (R$ %.2f)',\n                (SELECT nome_depto FROM departamentos WHERE id_depto = NEW.id_depto),\n                v_salario_total_depto, v_orcamento_depto;\n        END IF;\n    END IF;\n    \n    -- Se passou por todas as valida\u00e7\u00f5es, permite a opera\u00e7\u00e3o\n    RETURN NEW;\nEND;\n$$;\n\n-- 22. Cria\u00e7\u00e3o do trigger de valida\u00e7\u00e3o (BEFORE UPDATE)\nCREATE TRIGGER trg_valida_aumento_salarial\n    BEFORE UPDATE OF salario, id_depto\n    ON funcionarios\n    FOR EACH ROW\n    EXECUTE FUNCTION fn_valida_aumento_salarial();<\/code><\/pre>\n<p>Observe a cl\u00e1usula <strong>UPDATE OF salario, id_depto<\/strong> no trigger \u2014 ela restringe a execu\u00e7\u00e3o apenas quando essas colunas espec\u00edficas s\u00e3o mencionadas no comando UPDATE. Isso \u00e9 uma otimiza\u00e7\u00e3o importante: se um UPDATE modificar apenas o email ou o nome, nosso trigger de valida\u00e7\u00e3o n\u00e3o ser\u00e1 disparado, evitando processamento desnecess\u00e1rio. O PostgreSQL \u00e9 inteligente o suficiente para comparar a lista de colunas do UPDATE com a lista especificada no trigger e s\u00f3 invocar a fun\u00e7\u00e3o se houver interse\u00e7\u00e3o.<\/p>\n<p>Vamos testar a regra de neg\u00f3cio com alguns cen\u00e1rios: primeiro, um aumento dentro do limite (deve funcionar); depois, um aumento que excede 50% (deve ser rejeitado); por fim, um aumento que, embora dentro do limite, estouraria o or\u00e7amento do departamento.<\/p>\n<pre><code>-- 23. Teste 1: Aumento de 10% (dentro do limite) - Deve funcionar\n-- Fernando Lima: sal\u00e1rio atual 5500.00, novo: 6050.00 (aumento de 10%)\nUPDATE funcionarios\nSET salario = 6050.00\nWHERE id_func = 6;\n\n-- Verifica\u00e7\u00e3o\nSELECT id_func, nome, salario FROM funcionarios WHERE id_func = 6;<\/code><\/pre>\n<pre><code class=\"output\"> id_func |     nome      | salario \n---------+---------------+---------\n       6 | Fernando Lima | 6050.00\n(1 row)<\/code><\/pre>\n<pre><code>-- 24. Teste 2: Aumento de 60% (acima do limite) - Deve ser rejeitado\n-- Tentativa de aumentar Fernando Lima de 6050.00 para 9680.00 (aumento de 60%)\nUPDATE funcionarios\nSET salario = 9680.00\nWHERE id_func = 6;<\/code><\/pre>\n<pre><code class=\"output\">ERROR:  Aumento de 60.00% excede o limite m\u00e1ximo de 50% para o funcion\u00e1rio ID 6 (de R$ 6050.00 para R$ 9680.00)\nCONTEXT:  PL\/pgSQL function fn_valida_aumento_salarial() line 24 at RAISE<\/code><\/pre>\n<p>O trigger barrou a opera\u00e7\u00e3o com uma mensagem de erro clara e informativa, indicando exatamente qual foi o percentual excedido e quais valores estavam envolvidos. Essa pr\u00e1tica de fornecer mensagens detalhadas nas exce\u00e7\u00f5es \u00e9 algo que enfatizamos constantemente nos treinamentos da JRT Technology Solutions \u2014 um erro gen\u00e9rico como &#8220;Opera\u00e7\u00e3o inv\u00e1lida&#8221; n\u00e3o ajuda ningu\u00e9m a diagnosticar o problema, enquanto uma mensagem espec\u00edfica acelera a resolu\u00e7\u00e3o.<\/p>\n<pre><code>-- 25. Teste 3: Aumento que estouraria o or\u00e7amento do departamento\n-- Vamos verificar o or\u00e7amento atual do Financeiro (id_depto=2)\nSELECT nome_depto, orcamento FROM departamentos WHERE id_depto = 2;\n\n-- E o total de sal\u00e1rios atual do Financeiro\nSELECT SUM(salario + bonus) AS total_salarios\nFROM funcionarios\nWHERE id_depto = 2;\n\n-- Tentar aumentar o sal\u00e1rio para um valor que exceda o or\u00e7amento\n-- Financeiro: orcamento 800000.00, funcion\u00e1rios: Bruno (7000) + Fernando (6050)\n-- Total atual: 13050. Se aumentarmos Bruno para 790000.00, estoura\nUPDATE funcionarios\nSET salario = 790000.00\nWHERE id_func = 2;<\/code><\/pre>\n<pre><code class=\"output\"> nome_depto | orcamento  \n------------+------------\n Financeiro | 800000.00\n(1 row)\n\n total_salarios \n----------------\n       13050.00\n(1 row)\n\nERROR:  O sal\u00e1rio total do departamento \"Financeiro\" (R$ 796050.00) excederia o or\u00e7amento dispon\u00edvel (R$ 800000.00)\nCONTEXT:  PL\/pgSQL function fn_valida_aumento_salarial() line 48 at RAISE<\/code><\/pre>\n<p>Perfeito! Nosso trigger de valida\u00e7\u00e3o funcionou em todos os cen\u00e1rios. Ele permitiu o aumento leg\u00edtimo, bloqueou o aumento abusivo e tamb\u00e9m impediu a viola\u00e7\u00e3o or\u00e7ament\u00e1ria. Note que as valida\u00e7\u00f5es foram executadas antes da opera\u00e7\u00e3o (<strong>BEFORE UPDATE<\/strong>), garantindo que nenhum dado inv\u00e1lido jamais seja gravado na tabela.<\/p>\n<h3>Triggers Condicionais com WHEN: Filtrando Eventos para Performance<\/h3>\n<p>Em tabelas com milh\u00f5es de linhas e milhares de atualiza\u00e7\u00f5es por segundo, cada milissegundo gasto na execu\u00e7\u00e3o de triggers importa. \u00c9 por isso que o PostgreSQL oferece a cl\u00e1usula <strong>WHEN<\/strong>, que permite especificar uma condi\u00e7\u00e3o booleana que \u00e9 avaliada <em>antes<\/em> de invocar a fun\u00e7\u00e3o gatilho. Se a condi\u00e7\u00e3o for falsa, o trigger simplesmente n\u00e3o \u00e9 executado \u2014 a fun\u00e7\u00e3o nem chega a ser chamada. Isso \u00e9 drasticamente mais eficiente do que verificar a condi\u00e7\u00e3o dentro da fun\u00e7\u00e3o com um IF.<\/p>\n<p>Vamos criar um exemplo pr\u00e1tico: um trigger que registra na tabela de auditoria apenas as altera\u00e7\u00f5es salariais que ultrapassam R$ 1.000,00 de diferen\u00e7a (para mais ou para menos). Pequenas corre\u00e7\u00f5es salariais n\u00e3o precisam ser auditadas com tanto detalhe, mas varia\u00e7\u00f5es significativas sim. Implementaremos isso com um trigger <strong>AFTER UPDATE<\/strong> e a cl\u00e1usula <strong>WHEN<\/strong> que verifica a diferen\u00e7a absoluta entre o sal\u00e1rio novo e o antigo.<\/p>\n<pre><code>-- 26. Fun\u00e7\u00e3o gatilho para auditoria de grandes varia\u00e7\u00f5es salariais\nCREATE OR REPLACE FUNCTION fn_audita_grandes_variacoes()\nRETURNS TRIGGER\nLANGUAGE plpgsql\nAS $$\nBEGIN\n    -- Registra apenas a informa\u00e7\u00e3o relevante: a varia\u00e7\u00e3o salarial\n    INSERT INTO audit_funcionarios (operacao, id_func,\n                                    salario_old, salario_new,\n                                    usuario_bd,\n                                    data_operacao)\n    VALUES ('BIGVAR', NEW.id_func,\n            OLD.salario, NEW.salario,\n            CURRENT_USER,\n            CURRENT_TIMESTAMP);\n    RETURN NEW;\nEND;\n$$;\n\n-- 27. Cria\u00e7\u00e3o do trigger condicional com WHEN\n-- A condi\u00e7\u00e3o: a diferen\u00e7a absoluta entre sal\u00e1rio novo e antigo \u00e9 >= 1000\n-- Usamos a fun\u00e7\u00e3o ABS() e o operador de diferen\u00e7a absoluta\n-- OLD e NEW est\u00e3o dispon\u00edveis na cl\u00e1usula WHEN (exce\u00e7\u00e3o \u00e0 regra geral de PL\/pgSQL)\nCREATE TRIGGER trg_audita_grandes_variacoes\n    AFTER UPDATE OF salario\n    ON funcionarios\n    FOR EACH ROW\n    WHEN (ABS(NEW.salario - OLD.salario) >= 1000.00)\n    EXECUTE FUNCTION fn_audita_grandes_variacoes();<\/code><\/pre>\n<p>A grande vantagem aqui est\u00e1 na cl\u00e1usula <strong>WHEN (ABS(NEW.salario &#8211; OLD.salario) >= 1000.00)<\/strong>. Para todas as atualiza\u00e7\u00f5es onde a varia\u00e7\u00e3o salarial for menor que R$ 1.000,00, o PostgreSQL nem chega a invocar a fun\u00e7\u00e3o \u2014 a economia de chamadas de fun\u00e7\u00e3o e de contexto de trigger \u00e9 significativa em opera\u00e7\u00f5es em lote. Vamos testar com duas atualiza\u00e7\u00f5es: uma pequena (que n\u00e3o deve gerar auditoria) e uma grande (que deve gerar).<\/p>\n<pre><code>-- 28. Teste 1: Pequena varia\u00e7\u00e3o (menos de 1000) - N\u00c3O deve gerar auditoria\n-- Bruno Costa: sal\u00e1rio atual 7000.00, novo: 7500.00 (diferen\u00e7a = 500)\nUPDATE funcionarios\nSET salario = 7500.00\nWHERE id_func = 2;\n\n-- 29. Teste 2: Grande varia\u00e7\u00e3o (mais de 1000) - DEVE gerar auditoria\n-- Elena Santos: sal\u00e1rio atual 9100.00, novo: 10500.00 (diferen\u00e7a = 1400)\nUPDATE funcionarios\nSET salario = 10500.00\nWHERE id_func = 5;\n\n-- 30. Consulta \u00e0 tabela de auditoria filtrando apenas 'BIGVAR'\nSELECT operacao, id_func, salario_old, salario_new, \n       (salario_new - salario_old) AS diferenca\nFROM audit_funcionarios\nWHERE operacao = 'BIGVAR'\nORDER BY id_audit;<\/code><\/pre>\n<pre><code class=\"output\"> operacao | id_func | salario_old | salario_new | diferenca \n----------+---------+-------------+-------------+-----------\n BIGVAR   |       5 |     9100.00 |    10500.00 |   1400.00\n(1 row)<\/code><\/pre>\n<p>Conforme esperado, apenas a grande varia\u00e7\u00e3o (Elena Santos, diferen\u00e7a de R$ 1.400,00) foi registrada. A altera\u00e7\u00e3o de Bruno Costa (diferen\u00e7a de R$ 500,00) passou despercebida pelo trigger condicional. Se voc\u00ea consultar a tabela de auditoria completa, ver\u00e1 que outras opera\u00e7\u00f5es continuam registradas normalmente (INSERT, DELETE e UPDATE foram capturados pelo trigger de auditoria geral que criamos anteriormente), mas a linha espec\u00edfica de<\/p>\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>Aprenda a automatizar regras de neg\u00f3cio com triggers no PostgreSQL. Domine essa t\u00e9cnica essencial no nosso curso &#8220;PostgreSQL \u2014 Do Zero ao Avan\u00e7ado&#8221;!<\/p>\n","protected":false},"author":1,"featured_media":1682,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"iawp_total_views":1,"footnotes":""},"categories":[75],"tags":[2755,88,87,2757,2756,2754],"class_list":["post-1683","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops","tag-automacao-de-banco-de-dados","tag-banco-de-dados-relacional","tag-postgresql","tag-programacao-em-postgresql","tag-regras-de-negocio-postgresql","tag-triggers-no-postgresql"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"Aprenda a automatizar regras de neg\u00f3cio com triggers no PostgreSQL. Domine essa t\u00e9cnica essencial no nosso curso &quot;PostgreSQL \u2014 Do Zero ao Avan\u00e7ado&quot;!\" \/>\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\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/\" \/>\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 17: Triggers no PostgreSQL \u2014 automa\u00e7\u00e3o de regras de neg\u00f3cio - BLOG - JRT Technology Solutions\" \/>\n\t\t<meta property=\"og:description\" content=\"Aprenda a automatizar regras de neg\u00f3cio com triggers no PostgreSQL. Domine essa t\u00e9cnica essencial no nosso curso &quot;PostgreSQL \u2014 Do Zero ao Avan\u00e7ado&quot;!\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/jrtx.com.br\/blog\/2026\/07\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/\" \/>\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-20T21:21:36+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-07-20T21:21:36+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 17: Triggers no PostgreSQL \u2014 automa\u00e7\u00e3o de regras de neg\u00f3cio - BLOG - JRT Technology Solutions\" \/>\n\t\t<meta name=\"twitter:description\" content=\"Aprenda a automatizar regras de neg\u00f3cio com triggers no PostgreSQL. Domine essa t\u00e9cnica essencial no nosso curso &quot;PostgreSQL \u2014 Do Zero ao Avan\u00e7ado&quot;!\" \/>\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\\\/20\\\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\\\/#blogposting\",\"name\":\"Aula 17: Triggers no PostgreSQL \\u2014 automa\\u00e7\\u00e3o de regras de neg\\u00f3cio - BLOG - JRT Technology Solutions\",\"headline\":\"Aula 17: Triggers no PostgreSQL \\u2014 automa\\u00e7\\u00e3o de regras de neg\\u00f3cio\",\"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-1784582488988.jpg\",\"width\":1440,\"height\":1024,\"caption\":\"Aula 17: Triggers no PostgreSQL \\u2014 automa\\u00e7\\u00e3o de regras de neg\\u00f3cio\"},\"datePublished\":\"2026-07-20T18:21:36-03:00\",\"dateModified\":\"2026-07-20T18:21:36-03:00\",\"inLanguage\":\"pt-BR\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/20\\\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/20\\\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\\\/#webpage\"},\"articleSection\":\"DevOps, automa\\u00e7\\u00e3o de banco de dados, banco de dados relacional, PostgreSQL, programa\\u00e7\\u00e3o em PostgreSQL, regras de neg\\u00f3cio PostgreSQL, triggers no PostgreSQL\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/20\\\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\\\/#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\\\/20\\\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\\\/#listItem\",\"name\":\"Aula 17: Triggers no PostgreSQL \\u2014 automa\\u00e7\\u00e3o de regras de neg\\u00f3cio\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/20\\\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\\\/#listItem\",\"position\":3,\"name\":\"Aula 17: Triggers no PostgreSQL \\u2014 automa\\u00e7\\u00e3o de regras de neg\\u00f3cio\",\"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\\\/20\\\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\\\/#organizationLogo\",\"width\":100,\"height\":75},\"image\":{\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/20\\\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\\\/#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\\\/20\\\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\\\/#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\\\/20\\\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\\\/#webpage\",\"url\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/20\\\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\\\/\",\"name\":\"Aula 17: Triggers no PostgreSQL \\u2014 automa\\u00e7\\u00e3o de regras de neg\\u00f3cio - BLOG - JRT Technology Solutions\",\"description\":\"Aprenda a automatizar regras de neg\\u00f3cio com triggers no PostgreSQL. Domine essa t\\u00e9cnica essencial no nosso curso \\\"PostgreSQL \\u2014 Do Zero ao Avan\\u00e7ado\\\"!\",\"inLanguage\":\"pt-BR\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/20\\\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\\\/#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-1784582488988.jpg\",\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/20\\\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\\\/#mainImage\",\"width\":1440,\"height\":1024,\"caption\":\"Aula 17: Triggers no PostgreSQL \\u2014 automa\\u00e7\\u00e3o de regras de neg\\u00f3cio\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/jrtx.com.br\\\/blog\\\/2026\\\/07\\\/20\\\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\\\/#mainImage\"},\"datePublished\":\"2026-07-20T18:21:36-03:00\",\"dateModified\":\"2026-07-20T18:21:36-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 17: Triggers no PostgreSQL \u2014 automa\u00e7\u00e3o de regras de neg\u00f3cio - BLOG - JRT Technology Solutions","description":"Aprenda a automatizar regras de neg\u00f3cio com triggers no PostgreSQL. Domine essa t\u00e9cnica essencial no nosso curso \"PostgreSQL \u2014 Do Zero ao Avan\u00e7ado\"!","canonical_url":"https:\/\/jrtx.com.br\/blog\/2026\/07\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/","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\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/#blogposting","name":"Aula 17: Triggers no PostgreSQL \u2014 automa\u00e7\u00e3o de regras de neg\u00f3cio - BLOG - JRT Technology Solutions","headline":"Aula 17: Triggers no PostgreSQL \u2014 automa\u00e7\u00e3o de regras de neg\u00f3cio","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-1784582488988.jpg","width":1440,"height":1024,"caption":"Aula 17: Triggers no PostgreSQL \u2014 automa\u00e7\u00e3o de regras de neg\u00f3cio"},"datePublished":"2026-07-20T18:21:36-03:00","dateModified":"2026-07-20T18:21:36-03:00","inLanguage":"pt-BR","mainEntityOfPage":{"@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/#webpage"},"isPartOf":{"@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/#webpage"},"articleSection":"DevOps, automa\u00e7\u00e3o de banco de dados, banco de dados relacional, PostgreSQL, programa\u00e7\u00e3o em PostgreSQL, regras de neg\u00f3cio PostgreSQL, triggers no PostgreSQL"},{"@type":"BreadcrumbList","@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/#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\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/#listItem","name":"Aula 17: Triggers no PostgreSQL \u2014 automa\u00e7\u00e3o de regras de neg\u00f3cio"},"previousItem":{"@type":"ListItem","@id":"https:\/\/jrtx.com.br\/blog#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/#listItem","position":3,"name":"Aula 17: Triggers no PostgreSQL \u2014 automa\u00e7\u00e3o de regras de neg\u00f3cio","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\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/#organizationLogo","width":100,"height":75},"image":{"@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/#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\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/#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\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/#webpage","url":"https:\/\/jrtx.com.br\/blog\/2026\/07\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/","name":"Aula 17: Triggers no PostgreSQL \u2014 automa\u00e7\u00e3o de regras de neg\u00f3cio - BLOG - JRT Technology Solutions","description":"Aprenda a automatizar regras de neg\u00f3cio com triggers no PostgreSQL. Domine essa t\u00e9cnica essencial no nosso curso \"PostgreSQL \u2014 Do Zero ao Avan\u00e7ado\"!","inLanguage":"pt-BR","isPartOf":{"@id":"https:\/\/jrtx.com.br\/blog\/#website"},"breadcrumb":{"@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/#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-1784582488988.jpg","@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/#mainImage","width":1440,"height":1024,"caption":"Aula 17: Triggers no PostgreSQL \u2014 automa\u00e7\u00e3o de regras de neg\u00f3cio"},"primaryImageOfPage":{"@id":"https:\/\/jrtx.com.br\/blog\/2026\/07\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/#mainImage"},"datePublished":"2026-07-20T18:21:36-03:00","dateModified":"2026-07-20T18:21:36-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 17: Triggers no PostgreSQL \u2014 automa\u00e7\u00e3o de regras de neg\u00f3cio - BLOG - JRT Technology Solutions","og:description":"Aprenda a automatizar regras de neg\u00f3cio com triggers no PostgreSQL. Domine essa t\u00e9cnica essencial no nosso curso &quot;PostgreSQL \u2014 Do Zero ao Avan\u00e7ado&quot;!","og:url":"https:\/\/jrtx.com.br\/blog\/2026\/07\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/","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-20T21:21:36+00:00","article:modified_time":"2026-07-20T21:21:36+00:00","article:publisher":"https:\/\/www.facebook.com\/profile.php?id=61590814880509","twitter:card":"summary_large_image","twitter:title":"Aula 17: Triggers no PostgreSQL \u2014 automa\u00e7\u00e3o de regras de neg\u00f3cio - BLOG - JRT Technology Solutions","twitter:description":"Aprenda a automatizar regras de neg\u00f3cio com triggers no PostgreSQL. Domine essa t\u00e9cnica essencial no nosso curso &quot;PostgreSQL \u2014 Do Zero ao Avan\u00e7ado&quot;!","twitter:image":"https:\/\/jrtx.com.br\/blog\/wp-content\/uploads\/2026\/03\/cropped-logo-mini.jpg"},"aioseo_meta_data":{"post_id":"1683","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-30 23:02:57","updated":"2026-07-30 23:02:57","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 17: Triggers no PostgreSQL \u2014 automa\u00e7\u00e3o de regras de neg\u00f3cio\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 17: Triggers no PostgreSQL \u2014 automa\u00e7\u00e3o de regras de neg\u00f3cio","link":"https:\/\/jrtx.com.br\/blog\/2026\/07\/20\/aula-17-triggers-no-postgresql-automacao-de-regras-de-negoci\/"}],"_links":{"self":[{"href":"https:\/\/jrtx.com.br\/blog\/wp-json\/wp\/v2\/posts\/1683","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=1683"}],"version-history":[{"count":0,"href":"https:\/\/jrtx.com.br\/blog\/wp-json\/wp\/v2\/posts\/1683\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/jrtx.com.br\/blog\/wp-json\/wp\/v2\/media\/1682"}],"wp:attachment":[{"href":"https:\/\/jrtx.com.br\/blog\/wp-json\/wp\/v2\/media?parent=1683"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jrtx.com.br\/blog\/wp-json\/wp\/v2\/categories?post=1683"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jrtx.com.br\/blog\/wp-json\/wp\/v2\/tags?post=1683"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}