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.

Triggers – C++
Algo extremamente útil que você deve saber 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, aqui veremos como exemplo a IDE do IBM Data Studio que possui um wizard especifico para triggers.
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.

Cuidado com as Triggers
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
Visual Studio
Para efetuar as conexões com os diferentes bancos de dados você precisa primeiro configurar os drivers ODBC necessários no sistema operacional, depois você pode criar um design com 3 componentes RadioButton, 2 componentes Static Texts, 2 componentes Text Control, um componente Button e um componente CListControl, você encontra um walkthrough de como configurar os drivers ODBC na categoria SQL e C++ deste site, use a figura abaixo para referencia do design:

C++ – Design Time
IBM Data Studio
O IBM Data Studio possui uma funcionalidade especifica para criar triggers (Wizard), assim como possui para Stored Procedures e Function no qual necessitam de executar um Deploy antes de ser executada. Portanto você não poderá criar a Trigger utilizando a configuração Default do editor SQL comum, para isto você precisa mudar a configuração do terminador de instrução de ponto e virgula (;) para arroba(@), basta clicar com o botão direito do mouse e escolher a opção: Configurar Terminador de Instrução. Utilize a figura abaixo para referencia:

IBM Data Studio – Configuração
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;
DB2
create table Funcionarios (
ID_Funcionario INTEGER,
Nome VARCHAR(30),
Sobrenome VARCHAR(70),
Cargo VARCHAR(30),
Salario NUMERIC(9,2))@
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 INTEGER,
PORCENTAGEM DECIMAL(9,2))@
-- Cria tabela de lançamentos de descontos
Create table SALARIO (
ID_FUNCIONARIO INTEGER,
DATA_LANC DATE,
VDESCONTO DECIMAL(9,2))@
-- Deleta dados antigos
-- Caso utlizou exemplos anteriores
delete from desconto@
delete from salario@
-- Cria trigger na tabela Desconto
create TRIGGER DESCONTO_INSERT
BEFORE INSERT ON DESCONTO
REFERENCING NEW AS N
FOR EACH ROW
P1: BEGIN
-- Declara variáveis
DECLARE pID NUMBER;
DECLARE pSalario DECIMAL(9,2);
DECLARE pPorcentagem DECIMAL(9,2);
-- Alimenta variáveis com os valores a serem inseridos
SET pID = N.ID_FUNCIONARIO;
SET pPorcentagem = N.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 P1
MSSQL
create table Funcionarios (
ID_Funcionario Int,
Nome VARCHAR(30),
Sobrenome VARCHAR(70),
Cargo VARCHAR(30),
Salario Decimal(9,2));
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 INT,
PORCENTAGEM DECIMAL(9,2));
-- Cria tabela de lançamentos de descontos
Create table SALARIO (
ID_FUNCIONARIO INT,
DATA_LANC DATE,
VDESCONTO DECIMAL(9,2));
-- Deleta dados antigos
-- Caso utlizou exemplos anteriores
delete from desconto;
delete from salario;
-- Cria trigger na tabela Desconto
create TRIGGER DESCONTO_INSERT ON
DESCONTO AFTER INSERT AS
BEGIN
-- Declara variáveis
DECLARE @pID Int,
@pSalario DECIMAL(9,2),
@pPorcentagem DECIMAL(9,2);
-- Alimenta variáveis com os valores a serem inseridos
Select @pID = ID_FUNCIONARIO,
@pPorcentagem = PORCENTAGEM
from inserted;
-- Seleciona Salario do funcionario corrente
Select @pSalario = SALARIO FROM FUNCIONARIOS
WHERE ID_FUNCIONARIO = @pID;
-- insere na tabela de lançamentos de salario
Insert into SALARIO
values (
@pID,
GETDATE(),
(@pSalario * @pPorcentagem)/100);
END;
C++
Classe – AcessocppDlg.h
// Classe gerada automaticamente
// AcessocppDlg.h : header file
//
#pragma once
// Inclui classe de banco de dados MFC
#include "afxdb.h"
#include "afxcmn.h"
#include "afxwin.h"
class CAcessocppDlg : public CDialogEx
{
public:
CAcessocppDlg(CWnd* pParent = NULL); // standard constructor
enum { IDD = IDD_ACESSOCPP_DIALOG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
protected:
HICON m_hIcon;
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
// Cria métodos e objetos da classe
CDatabase db;
CString bancodedados;
void conectarDB(CString dns, CString usuario, CString senha);
afx_msg void OnBnHotItemChangeRadio1(NMHDR *pNMHDR, LRESULT *pResult);
afx_msg void OnBnHotItemChangeRadio2(NMHDR *pNMHDR, LRESULT *pResult);
afx_msg void OnBnHotItemChangeRadio3(NMHDR *pNMHDR, LRESULT *pResult);
afx_msg void OnBnClickedButton1();
CListCtrl m_tabela;
CEdit m_funcionario;
CEdit m_porcentagem;
};
Classe – AcessocppDlg.cpp
// Código gerado automaticamente
// AcessocppDlg.cpp : implementation file
//
#include "stdafx.h"
#include "Acessocpp.h"
#include "AcessocppDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX);
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
CAcessocppDlg::CAcessocppDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CAcessocppDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CAcessocppDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST2, m_tabela);
DDX_Control(pDX, IDC_EDIT1, m_funcionario);
DDX_Control(pDX, IDC_EDIT2, m_porcentagem);
}
BEGIN_MESSAGE_MAP(CAcessocppDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_NOTIFY(BCN_HOTITEMCHANGE, IDC_RADIO1, &CAcessocppDlg::OnBnHotItemChangeRadio1)
ON_NOTIFY(BCN_HOTITEMCHANGE, IDC_RADIO2, &CAcessocppDlg::OnBnHotItemChangeRadio2)
ON_NOTIFY(BCN_HOTITEMCHANGE, IDC_RADIO3, &CAcessocppDlg::OnBnHotItemChangeRadio3)
ON_BN_CLICKED(IDC_BUTTON1, &CAcessocppDlg::OnBnClickedButton1)
END_MESSAGE_MAP()
BOOL CAcessocppDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
SetIcon(m_hIcon, TRUE);
SetIcon(m_hIcon, FALSE);
// Desenvolvimento Aberto
// Inicializa dialogo
// Define variavel padrão para o banco de dados
bancodedados = "oracle";
// Cria o modelo de exibição de dados
m_tabela.SetView(LV_VIEW_DETAILS);
m_tabela.SendMessage(LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_GRIDLINES);
return TRUE;
}
void CAcessocppDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
void CAcessocppDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this);
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
HCURSOR CAcessocppDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
// **************************
// * Desenvolvimento Aberto *
// **************************
// Nosso código começa aqui
// Seleciona banco de dados Oracle
void CAcessocppDlg::OnBnHotItemChangeRadio1(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMBCHOTITEM pHotItem = reinterpret_cast<LPNMBCHOTITEM>(pNMHDR);
bancodedados = "oracle";
*pResult = 0;
}
// Seleciona banco de dados IBM DB2
void CAcessocppDlg::OnBnHotItemChangeRadio2(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMBCHOTITEM pHotItem = reinterpret_cast<LPNMBCHOTITEM>(pNMHDR);
bancodedados = "db2";
*pResult = 0;
}
// Seleciona banco de dados MSSQL
void CAcessocppDlg::OnBnHotItemChangeRadio3(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMBCHOTITEM pHotItem = reinterpret_cast<LPNMBCHOTITEM>(pNMHDR);
bancodedados = "mssql";
*pResult = 0;
}
// Evento de clique do botão
void CAcessocppDlg::OnBnClickedButton1()
{
// campo de dado da tabela
CString m_campo;
// Abre conexão
if (bancodedados == "oracle")
{
conectarDB(L"OracleXE", L"daberto", L"p@55w0rd");
}
if (bancodedados == "db2")
{
conectarDB(L"IBMDB2", L"db2admin", L"p@55w0rd");
}
if (bancodedados == "mssql")
{
conectarDB(L"MSSQLSERVER", L"devaberto", L"p@55w0rd");
}
// Cria um set de dados
CRecordset dados(&db);
// Cria variáveis de entrada
CString m_func;
CString m_porc;
// Recupera valor de entrada
m_funcionario.GetWindowTextW(m_func);
m_porcentagem.GetWindowTextW(m_porc);
// Cria instrução SQL para disparar a trigger
CString sqltrigger = L"insert into DESCONTO VALUES (" + m_func + ", " + m_porc + ")";
// Executa instrução SQL
db.ExecuteSQL(sqltrigger);
CString sql = L"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";
dados.Open(CRecordset::forwardOnly, sql);
// Cria item
LVITEM lvItem;
// cria estrutura para inserir o item
typedef struct _LVITEM
{
UINT mask;
int iItem;
int iSubItem;
UINT state;
UINT stateMask;
LPTSTR pszText;
int cchTextMax;
int iImage;
LPARAM lParam;
#if (_WIN32_IE >= 0x0300)
int iIndent;
#endif
} LVITEM, FAR *LPLVITEM;
// Define variaveis de itens
int InsertItem(const LVITEM* pItem);
int nItem;
// Converte CString para LPTSTR atraves de um TCHAR
TCHAR sz[1024];
// Verifica colunas
short nFields = dados.GetODBCFieldCount();
int colunas = m_tabela.GetHeaderCtrl()->GetItemCount();
// Verifica colunas
if (colunas == 0)
{
// Lê metadata da tabela
CODBCFieldInfo field;
for (UINT i = 0; i < nFields; i ++)
{
dados.GetODBCFieldInfo(i, field);
m_tabela.InsertColumn(i, field.m_strName, LVCFMT_LEFT, 100);
}
}
// Deleta itens do controle de lista
m_tabela.DeleteAllItems();
// Recupera dados da tabela
while (!dados.IsEOF())
{
for (short index = 0; index < nFields; index++)
{
dados.GetFieldValue(index, m_campo);
// Retorna linha do banco de dados
if (index == 0)
{
// Insere linha
lvItem.mask = LVIF_TEXT;
lvItem.iItem = 0;
lvItem.iSubItem = 0;
lvItem.pszText = lstrcpy(sz, m_campo);
nItem = m_tabela.InsertItem(&lvItem);
}
// Retorna colunas da linha
m_tabela.SetItemText(nItem, index, lstrcpy(sz, m_campo));
}
// Move o cursor para a proxima linha
dados.MoveNext();
}
// Fecha o set de dados e a conexão
dados.Close();
db.Close();
}
void CAcessocppDlg::conectarDB(CString dns, CString usuario, CString senha)
{
// Cria string de conexão ODBC
CString conexao;
// Cria string de conexão
conexao = L"DSN=" + dns + L";UID=" + usuario + L";PWD=" + senha;
// Abre conexão
db.OpenEx(conexao, 0);
}