Uma Trigger ou gatilho é um tipo especial de procedimento armazenado (Stored Procedure) que executa automaticamente quando um evento ocorre no servidor de banco de dados. Gatilhos DML executam quando um usuário tenta modificar dados através de uma linguagem de manipulação de dados (DML). Eventos DML são INSERT, UPDATE, ou DELETE em uma tabela ou exibição. Esses gatilhos disparam quando qualquer evento válido é acionado, independentemente de haver ou não linhas da tabela afetadas.
As Triggers possuem muitas diferenças de sintaxe e funcionalidades de um banco de dados para outro, por exemplo Trigger que dispara o evento INSTEAD OF INSERT (ao invés de inserir) no banco de dados Oracle só pode ser declarada sobre uma VIEW enquanto em MSSQL Server pode ser declarada em uma tabela comum, o evento BEFORE INSERT (antes de inserir) existe nos bancos de dados Oracle e IBM DB2, mas não existe no MSSQL. Por este motivo é recomendado que você procure os links oficiais para saber mais detalhes sobre as Triggers.
Algo extremamente útil sobre Triggers
As Triggers foram regulamentadas na revisão da língua SQL em 1999 e passou a ser padrão em banco de dados relacionais, no entanto alguns bancos de dados vem atualizando estes recursos regularmente, por exemplo o Oracle utiliza Triggers que disparam por esquema (Schema-level triggers) desde a sua versão 9i, enquanto o MSSQL suporta trigger do tipo DDL, ou logon trigger apenas desde a versão 2008.
Muitos sistemas de grande porte como SAP e vários outros não utilizam triggers, principalmente sistemas que no qual rodam em diversos bancos de dados, estas são consideradas verdadeiras armadilhas por muitos DBAs, podem ser facilmente esquecidas na hora de alterações e se tornarem invalidas (Oracle), gerando assim muitos erros caso não haja uma boa politica de desenvolvimento e qualidade (testes unitários e integrados) antes de colocar as rotinas do sistema em produção. Mesmo as IDEs de estudios SQL já mudaram varias vezes a localização do node de triggers ao longo de suas versões para tentar facilitar a manutenção das mesmas.
As Triggers também devem ser desenvolvidas com cuidado sempre seguindo as melhores praticas, pois o desenvolvimento SQL é muito abrangentes e cheio de recursos, então é fácil extrapolar e criar triggers contendo cursores, stored procedures, rollbacks, acessos repetitivos, outras triggers, lembre-se da lei de Murphy, tudo que não pode ser escrito em uma trigger, será e isto reduz sensivelmente a performance de um sistema, sem contar com a complexidade do desenvolvimento, transformado seu desenvolvimento SQL em verdadeiros planos da ACME, lembre-se do coite que tentava incessantemente capturar o papa-léguas.
Nunca use Triggers para criar campos auto incremento para os códigos (ID), os bancos de dados possuem recursos específicos para este trabalho, o uso de triggers somente criará acessos e UPDATES desnecessários no banco de dados, além de reduzir a performance, imagine importar 10.000 linhas utilizando um Insert…Select, à partir de uma outra tabela ou arquivos externos, você efetuara um Select e um Update para cada registro se utilizar triggers, enquanto os bancos de dados contam com seus campos auto incremento que já nos permitem utilizar dados em massa e inserir as 10.000 linhas em um único acesso.
As Triggers também são um alto risco a segurança, é possível escalar privilégios e se tornar proprietário do banco de dados e do servidor SQL caso você insira um código mal intencionado em uma Trigger, exemplo: suponha que o usuário Jõao da Silva tenha diretos de Administrador do banco de dados, se um desenvolvedor mal intencionado criar uma trigger DDL sobre o evento DELETE de uma tabela qualquer utilizando por exemplo a instrução: GRANT CONTROL SERVER TO BadUser; Quando o usuário João da Silva executar o procedimento que dispara a trigger o Grant será delegado ao usuário mal intencionado e os direitos aplicados a ele, enquanto ele mesmo não teria acesso para delegar tal direito, deste modo possibilitando que o desenvolvedor mal intencionado tome conta do servidor SQL, podendo fazer o que bem entender com os dados.
Security: http://msdn.microsoft.com/pt-br/library/ms191134.aspx
Exemplo:
Neste exemplo criamos uma funcionalidade similar do nosso exemplo anterior (Cursor), utilizando a mesma modelagem de dados, porem para executar este procedimento as tabelas DESCONTO e SALARIO devem estar vazias, o usuário entrará com o código do funcionário e a porcentagem do desconto e a trigger sobre a tabela DESCONTO se encarregara de criar o lançamento na tabela de SALARIO.
SQL
Oracle
create table Funcionarios( ID_Funcionario NUMBER(5), Nome VARCHAR2(30), Sobrenome VARCHAR2(70), Cargo VARCHAR2(30), Salario NUMBER(9,2)); -- Cria Funcionarios Insert into FUNCIONARIOS values (1,'Steve','Gates','Programador',2550.56); Insert into FUNCIONARIOS values (2,'Bill','Jobs','Diretor',5143.71); Insert into FUNCIONARIOS values (3,'Wozniak','Gates','Desenvolvedor', 4389.21); -- Cria tabela com a porcentagem de descontos Create table DESCONTO ( ID_FUNCIONARIO NUMBER, PORCENTAGEM NUMBER(9,2)); -- Cria tabela de lançamentos de descontos Create table SALARIO ( ID_FUNCIONARIO NUMBER, DATA_LANC DATE, VDESCONTO NUMBER(9,2)); -- Deleta dados antigos -- Caso utlizou exemplos anteriores delete from desconto; delete from salario; -- Cria trigger na tabela Desconto create or replace TRIGGER DESCONTO_INSERT BEFORE INSERT ON DESCONTO FOR EACH ROW -- Declara variáveis DECLARE pID NUMBER; pSalario NUMBER(9,2); pPorcentagem NUMBER(9,2); BEGIN -- Alimenta variáveis com os valores a serem inseridos pID := :new.ID_FUNCIONARIO; pPorcentagem := :new.PORCENTAGEM; -- Seleciona Salario do funcionario corrente Select SALARIO INTO pSalario FROM FUNCIONARIOS WHERE ID_FUNCIONARIO = pID; -- insere na tabela de lançamentos de salario Insert into SALARIO values ( pID, SYSDATE, (pSalario * pPorcentagem)/100); END DESCONTO_INSERT;
Python
#!/usr/bin/env python # -*- coding: latin-1 -*- # Desenvolvimento Aberto # TriggerOracle.py # importa modulos import wx import wx.grid import cx_Oracle # Cria classe generica de uma WX.Grid # A classe abaixo faz parte da documentação WXPython oficial # Este trecho de código é util para manipular a grade class GenericTable(wx.grid.PyGridTableBase): def __init__(self, data, rowLabels=None, colLabels=None): wx.grid.PyGridTableBase.__init__(self) self.data = data self.rowLabels = rowLabels self.colLabels = colLabels def GetNumberRows(self): return len(self.data) def GetNumberCols(self): return len(self.data[0]) def GetColLabelValue(self, col): if self.colLabels: return self.colLabels[col] def GetRowLabelValue(self, row): if self.rowLabels: return self.rowLabels[row] def IsEmptyCell(self, row, col): return False def GetValue(self, row, col): return self.data[row][col] def SetValue(self, row, col, value): pass # Inicializa Grade dados = [] colLabels = [] rowLabels = ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10") # Cria conexão def conectarORA(): sconexao = "user/password@localhost/XE" try: con = cx_Oracle.connect(sconexao) except ValueError: tkMessageBox.showinfo(title="Menssagem", message="Erro de Conexão", parent=janela) return con # Executa e retorna SQL def retornaTabelaORA(sql, con): cursor = con.cursor() cursor.execute(sql) return cursor def retornaDados(idfunc): # Cria conexão con = conectarORA() # Envia dados a grid sql = "Select A.ID_FUNCIONARIO, " + "A.NOME, " + " A.CARGO, " + \ " A.SALARIO, " + "B.PORCENTAGEM, " + "C.VDESCONTO, " + \ "C.DATA_LANC, " + "A.SALARIO - C.VDESCONTO AS SLIQUIDO " +\ "from FUNCIONARIOS A, DESCONTO B, SALARIO C " + "Where " + \ "A.ID_FUNCIONARIO = B.ID_FUNCIONARIO AND " + \ "A.ID_FUNCIONARIO = C.ID_FUNCIONARIO AND " + \ "A.ID_FUNCIONARIO = " + idfunc # retorna set de dados tabela = retornaTabelaORA(sql, con) # Retorna metadados da tabela for i in range(0, len(tabela.description)): colLabels.append(tabela.description[i][0]) # Executa um fecth em todos os registros resultado = tabela.fetchall() # Popula dados for conteudo in resultado: dados.append(conteudo) # Cria classe da grid class SimpleGrid(wx.grid.Grid): def __init__(self, parent): wx.grid.Grid.__init__(self, parent, -1, pos=(5,90), size=(850,200)) # Cria formulario class TestFrame(wx.Frame): def __init__(self, parent): wx.Frame.__init__(self, parent, -1, "Desenvolvimento Aberto - Triggers - Python", size=(900, 350)) panel = wx.Panel(self, wx.ID_ANY) label = wx.StaticText(panel, -1, label='Oracle Database - Triggers ->', pos=(300,10)) lfuncionario = wx.StaticText(panel, -1, label='Cod funcionario:', pos=(20,50)) self.funcionario = wx.TextCtrl(panel, size=(100, -1), pos=(120,50)) lporcentagem = wx.StaticText(panel, -1, label='Porcentagem (%):', pos=(260,50)) self.porcentagem = wx.TextCtrl(panel, size=(100, -1), pos=(360,50)) botao = wx.Button(panel, label="Inserir", pos=(480,50)) botao.Bind(wx.EVT_BUTTON, self.botaoInserir) self.grid = SimpleGrid(panel) #Insere dados e dispara trigger def botaoInserir(self,event): # Conecta e cria um cursor con = conectarORA() cursor = con.cursor() # Insere clausula SQL sql = "insert into DESCONTO VALUES (" + self.funcionario.GetValue() + ", " + self.porcentagem.GetValue() + ")"; # Executa e comita a transação cursor.execute(sql) con.commit() # Retorna set de dados retornaDados(self.funcionario.GetValue()) # Limpa grade self.grid.ClearGrid() # Insere set de dados contidos em um tuplas tableBase = GenericTable(dados, rowLabels, colLabels) self.grid.SetTable(tableBase) # Atualiza grade self.grid.ForceRefresh() # Inicializa a aplicação app = wx.PySimpleApp() frame = TestFrame(None) frame.Show(True) app.MainLoop()