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#

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

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 seu projeto com os assemblers ADO.NET  necessários e depois você pode criar um design com 3 componentes RadioButton, 2 componentes Labels, 2 componentes TextBoxes e um componente Button e um componente DataGridView use a figura abaixo para referencia:

Triggers - Design Time

Triggers – 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

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#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Data.SqlClient;
using Oracle.DataAccess.Client;
using IBM.Data.DB2; 

namespace Acesso
{
    public partial class Dados : Form
    {
        private static OracleConnection connORA; // ODAC 12c
        private static DB2Connection connDB2;   // IBM Data Server Provider
        private static SqlConnection connMSSQL; // ADO .NET

        DataTable dataTable;

        string sql;
        static string bancodedados;

        public Dados()
        {
            InitializeComponent();
        }

        // Cria métodos de conexão
        // Você pode optar por um unico método se usar apenas ADO.NET
        // Neste caso usamos os drivers nativos de conexão de cada banco de dados.
        // Todos os drivers são baseados em ADO .NET
        public void conexaoODAC(string Username, string Password, string Datasource)
        {
            try
            {
                // String de Conexao
                string connectionString =

                    // Usuario
                    "User Id=" + Username +

                    // Senha
                    ";Password=" + Password +

                    // TNSnames
                    ";Data Source=" + Datasource;

                //Conecta ao datasource usando a conexão Oracle
                 connORA = new OracleConnection(connectionString);

                //Abre a conexão com o banco de dados
                connORA.Open();

            }
            // Retorna erro
            catch (Exception ex)
            {
                // Mostra menssagem de erro
                MessageBox.Show(ex.ToString());

            }
        }

        void conexaoDB2(string Server, string Database,
                                    string Username, string Password, string Timeout)
        {
            try
            {
                // String de Conexao
                string connectionString =

                    // Servidor
                    "Server=" + Server +

                    // Banco de dados
                    ";Database=" + Database +

                    // Usuario
                    ";UID=" + Username +

                    // Senha
                    ";PWD=" + Password +

                    // TNSnames
                    ";Connect Timeout=" + Timeout;

                //Conecta ao datasource usando a conexão DB2
                connDB2 = new DB2Connection(connectionString);

                //Abre a conexão com o banco de dados
                connDB2.Open();                

            }
            // Retorna erro
            catch (Exception ex)
            {
                // Mostra menssagem de erro
                MessageBox.Show(ex.ToString());

            }
        }

         public void conexaoMSSQL(string Server, string Database,
                                     string Username, string Password, string Timeout)
         {
             try
             {
                 // String de Conexao
                 string connectionString =

                     // Servidor
                     "Data Source=" + Server +

                     // Banco de dados
                     ";Initial Catalog=" + Database +

                     // Usuario
                     ";User ID =" + Username +

                     // Senha
                     ";Password=" + Password +

                     // TNSnames
                     ";Connect Timeout=" + Timeout;

                 //Conecta ao datasource usando a conexão Padrão
                 connMSSQL = new SqlConnection(connectionString);

                 //Abre a conexão com o banco de dados
                 connMSSQL.Open();

             }
             // Retorna erro
             catch (Exception ex)
             {
                 // Mostra menssagem de erro
                 MessageBox.Show(ex.ToString());

             }
         }

        // Retorna um set de dados
         public  DataTable retornaTabela(string sql, string sqlTrigger)
         {
             // Declara comandos para Stored Procedure
             OracleCommand cmdORA;
             DB2Command cmdDB2;
             SqlCommand cmdSQL;

             // Cria instância da classe
             Dados acesso = new Dados();

             // Define banco de dados

             if (bancodedados == "oracle")
             {
                 // Efetua Login no banco de dados
                 acesso.conexaoODAC("daberto", "p@55w0rd", "XE");

                 // Define a instrução SQL e a conexão
                 cmdORA = new OracleCommand(sqlTrigger, connORA);

                 // Executa query
                 cmdORA.ExecuteNonQuery();

                 // Cria comandos para retornar dados para exibir a grade de dados
                 OracleCommand oracmd = new OracleCommand(sql, connORA);
                 OracleDataReader orareader = oracmd.ExecuteReader();
                 dataTable = new DataTable();
                 dataTable.Load(orareader);
             }

             if (bancodedados == "db2")
             {
                 acesso.conexaoDB2("localhost", "DEVA", "db2admin", "p@55w0rd", "40");

                 cmdDB2 = new DB2Command(sqlTrigger, connDB2);

                 cmdDB2.ExecuteNonQuery();

                 DB2Command db2cmd = new DB2Command(sql);
                 db2cmd.Connection = connDB2;
                 DB2DataReader db2reader = db2cmd.ExecuteReader();
                 dataTable = new DataTable();
                 dataTable.Load(db2reader);                

             }

             if (bancodedados == "mssql")
             {
                 acesso.conexaoMSSQL("localhost", "DevAberto", "devaberto", "p@55w0rd", "");

                 cmdSQL = new SqlCommand(sqlTrigger, connMSSQL);

                 cmdSQL.ExecuteNonQuery();

                 SqlCommand mssqlcmd = new SqlCommand(sql);
                 mssqlcmd.Connection = connMSSQL;
                 SqlDataReader mssqlreader = mssqlcmd.ExecuteReader();
                 dataTable = new DataTable();
                 dataTable.Load(mssqlreader);

             }

             return  dataTable;
         }

        // configura programa
        private void Form1_Shown(object sender, EventArgs e)
        {
            radioButton1.Checked = true;
            bancodedados = "oracle";
        }

        // Conecta dados
        private void button1_Click(object sender, EventArgs e)
        {
            // Cria instância da classe
            Dados dados = new Dados();

            // Insere Porcentagem de desconto e executa trigger
            string sqltrigger = "insert into DESCONTO VALUES (" + textBox1.Text +
                              ", " + textBox2.Text + ")";

            // Exibe relatorio de lançamentos
            string 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";

            // Alimenta grid de dados
            dataGridView1.DataSource = dados.retornaTabela(sql, sqltrigger);

        }

        // Seleciona banco de dados Oracle
        private void radioButton1_CheckedChanged(object sender, EventArgs e)
        {
            if (radioButton1.Checked)
            {
                bancodedados = "oracle";
            }

        }

        // Seleciona banco de dados IBM DB2
        private void radioButton2_CheckedChanged(object sender, EventArgs e)
        {
            if (radioButton2.Checked)
            {
                bancodedados = "db2";
            }
        }

        // Seleciona banco de dados MSSQL Server
        private void radioButton3_CheckedChanged(object sender, EventArgs e)
        {
            if (radioButton3.Checked)
            {
                bancodedados = "mssql";
            }
        }
    }
}

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 - Oracle - DB2 - MSSQL

Triggers – Oracle – DB2 – MSSQL

 

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

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

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

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;

Java

import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableModel;

public class Cursor implements ActionListener, ItemListener {
	// Cria componentes
	private JTable tabela;
	private JRadioButton banco1;
	private JRadioButton banco2;
	private JRadioButton banco3;
	private JButton botao;
	private JLabel lid_funcionario;
	private JLabel lporcentagem;
	private JTextField tid_funcionario;
	private JTextField tporcentagem;

	// Declara objetos de conexão
	private static Connection conn;
	private static Statement query;
	private static String bancodedados;

	public void conectar(String login, String senha) {

		// Verifica strings de conexão

		// ORACLE
		if (bancodedados == "oracle") {
			try {
				// Define Driver de conexão JDBC thin
				Class.forName("oracle.jdbc.driver.OracleDriver");
				conn = DriverManager.getConnection(
						"jdbc:oracle:thin:@localhost:1521:xe", login, senha);

				// Executa pedido SQL
				query = conn.createStatement();

			}

			catch (ClassNotFoundException ex) {
				ex.printStackTrace();
			}

			catch (SQLException ex) {
				ex.printStackTrace();
			}
		}

		// DB2
		if (bancodedados == "db2") {
			try {
				// Define Driver de conexão JDBC
				Class.forName("com.ibm.db2.jcc.DB2Driver");
				conn = DriverManager.getConnection(
						"jdbc:derby:net://localhost:50000/deva", login, senha);

				// Executa pedido SQL
				query = conn.createStatement();
				// JOptionPane.showMessageDialog(menssagem,
				// "Conexão Efetuada com sucesso!");
			}

			catch (ClassNotFoundException ex) {
				ex.printStackTrace();
			}

			catch (SQLException ex) {
				// JOptionPane.showMessageDialog(menssagem, "Erro na conexão!");
				ex.printStackTrace();
			}

		}

		// MICROSOFT SQL SERVER
		if (bancodedados == "mssql") {
			try {
				// Define Driver de conexão JDBC
				String URL = "jdbc:sqlserver://localhost\\SQLEXPRESS:1433;databaseName=devaberto"
						+ ";user=" + login + ";password=" + senha;

				Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
				conn = DriverManager.getConnection(URL);

				// Executa pedido SQL
				query = conn.createStatement();

			}

			catch (ClassNotFoundException ex) {
				ex.printStackTrace();
			}

			catch (SQLException ex) {
				ex.printStackTrace();
			}

		}

	}

	public void executaSQL(String sql)   {
		// Cria nova instrução SQL
		Statement trigger;
		;
		try {
			trigger = conn.createStatement();
			trigger.execute(sql);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public ResultSet retornaTabela() throws SQLException {
		// Cria nova instrução SQL
		Statement query;
		query = conn.createStatement();
		String sql;

		// Verfica banco de dados e passa script SQL
		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";

		// Executa Script
		ResultSet dados = query.executeQuery(sql);

		// Retorna set de dados
		return dados;
	}

	// Modelo
	public static DefaultTableModel criaTableModel(ResultSet rs)
			throws SQLException {

		// Cria um modelo de tabela
		ResultSetMetaData metaData = rs.getMetaData();

		// Retorna as colunas
		Vector<String> colunas = new Vector<String>();

		int columnCount = metaData.getColumnCount();

		for (int column = 1; column <= columnCount; column++) {
			colunas.add(metaData.getColumnName(column));
		}

		// Retorna dados
		Vector<Vector<Object>> dados = new Vector<Vector<Object>>();

		while (rs.next()) {
			Vector<Object> vector = new Vector<Object>();

			for (int columnIndex = 1; columnIndex <= columnCount; columnIndex++) {
				vector.add(rs.getObject(columnIndex));
			}
			dados.add(vector);
		}

		return new DefaultTableModel(dados, colunas);

	}

	public void itemStateChanged(ItemEvent arg0) {

		// Verifica item banco de dados selecionado
		Object fonte = arg0.getItemSelectable();
		int estado = arg0.getStateChange();

		if (estado == arg0.SELECTED) {

			if (fonte == banco1) {
				bancodedados = "oracle";
			}

			if (fonte == banco2) {
				bancodedados = "db2";
			}

			if (fonte == banco3) {
				bancodedados = "mssql";
			}

		}

	}

	public void actionPerformed(ActionEvent arg0) {
		// Efetua login no banco de dados
		Cursor acesso = new Cursor();

		if (bancodedados == "oracle") {
			acesso.conectar("daberto", "p@55w0rd");
		}

		if (bancodedados == "db2") {
			acesso.conectar("db2admin", "p@55w0rd");
		}

		if (bancodedados == "mssql") {
			acesso.conectar("devaberto", "p@55w0rd");
		}

		try {
			// Executa Trigger (Tabela Desconto)

			acesso.executaSQL("insert into DESCONTO VALUES ("+ tid_funcionario.getText() +
					          ", " + tporcentagem.getText() + ")");

			// Executa Intrução SQL
			tabela.setModel(criaTableModel(acesso.retornaTabela()));
		} catch (SQLException e) {
			e.printStackTrace();
		}

	}

	public Container criaPainel() throws SQLException {
		// Cria painel principal
		JPanel painel = new JPanel();

		// Seleciona layout
		painel.setLayout(new BoxLayout(painel, BoxLayout.PAGE_AXIS));
		painel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));

		// Cria painel de escolha de conexão
		JPanel pescolha = new JPanel();

		pescolha.setLayout(new BoxLayout(pescolha, BoxLayout.LINE_AXIS));
		pescolha.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

		ButtonGroup grupo = new ButtonGroup();

		// Cria componentes de radio
		banco1 = new JRadioButton("Oracle");
		banco2 = new JRadioButton("IBM DB2");
		banco3 = new JRadioButton("Microsoft SQL");

		// Agrupa botões de radio
		grupo.add(banco1);
		grupo.add(banco2);
		grupo.add(banco3);

		// Cria ouvinte dos botões
		banco1.addItemListener(this);
		banco2.addItemListener(this);
		banco3.addItemListener(this);

		// Seleciona primeira conexão
		banco1.setSelected(true);

		// Adiciona botões ao painel
		pescolha.add(banco1);
		pescolha.add(banco2);
		pescolha.add(banco3);

		// Efetua primeira conexão ao banco (ORACLE)
		Cursor acesso = new Cursor();

		acesso.conectar("daberto", "myoracle123");

		// Cria modelo de tabela
		tabela = new JTable(criaTableModel(acesso.retornaTabela()));

		// Adiciona um painel de rolagem
		JScrollPane rolar = new JScrollPane(tabela);

		// Cria painel do botão
		JPanel pbotao = new JPanel(new GridLayout(0, 5, 10, 10));		

		// Cria botão
		lid_funcionario = new JLabel("Código Func:");
		lporcentagem = new JLabel("Porcentagem:");

		tid_funcionario = new JTextField();
		tporcentagem = new JTextField();

		botao = new JButton("Inserir");
		botao.addActionListener(this);

		// Define foco do cursor
		javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                tid_funcionario.requestFocus();
            }
        });

		// Adiciona botão ao painel
		pbotao.add(lid_funcionario);
		pbotao.add(tid_funcionario);
		pbotao.add(lporcentagem);
		pbotao.add(tporcentagem);
		pbotao.add(botao);

		// componentes ao painel principal
		painel.add(pescolha);
		painel.add(rolar);
		painel.add(Box.createVerticalStrut(10));
		painel.add(pbotao);

		return painel;
	}

	public static void criaGUI() {
		// Cria formulario
		JFrame formulario = new JFrame("Desenvolvimento Aberto - Triggers");
		formulario.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		// cria painel de conteudo
		Cursor acesso = new Cursor();

		try {
			formulario.setContentPane(acesso.criaPainel());
		}

		catch (SQLException e) {
			e.printStackTrace();
		}

		// Exibe o formulario
		formulario.setSize(700, 300);
		formulario.setVisible(true);
	}

	public static void main(String[] args) {
		javax.swing.SwingUtilities.invokeLater(new Runnable() {

			@Override
			public void run() {

				// Cria e mostra a GUI
				criaGUI();

			}
		});
	}
}

Você pode utilizar as funções NMIN e NMAX para retornar os valores mínimos e máximos entre pelo menos dois parâmetros. Estas funções fazem o mesmo trabalho da clausula IF efetuando comparações utilizando os sinais de maior (>) ou menor (<), porem possuem uma sintaxe muito menos complexa se utilizados muitos parâmetros.

Fora de uma expressão aritmética, um tipo de cálculo é determinado a partir de todos os argumentos, e é utilizado para realizar a comparação. O tipo de cálculo é determinado apenas como uma expressão aritmética e também determina o tipo do valor de retorno de dados.

Em uma expressão aritmética, os argumentos da função contribuem para o tipo de cálculo de toda a expressão e a função é calculada usando o tipo do cálculo. Se um argumento em si é uma expressão aritmética, os operandos contribuem para todo o tipo de cálculo e o argumento também é calculado usando esse mesmo tipo.

 

Funções - Extremum

Funções – Extremum

Exemplo:

Neste exemplo criamos uma tela de seleção com três parâmetros e as funções Extremum verificam e exibem uma mensagem retornando o maior e o menor parâmetro dentro das possibilidades alimentadas pelo usuário.

Abap

*&---------------------------------------------------------------------*
*& Report  ZMAXMIN
*&
*&---------------------------------------------------------------------*
*& Desenvolvimento Aberto
*& Numerical Extremum Functions
*&---------------------------------------------------------------------*

REPORT  ZMAXMIN.

* Cria parametros de seleção
SELECTION-SCREEN COMMENT /1(10) text-par.
PARAMETERS: valor1 TYPE i DEFAULT 0,
            valor2 TYPE i DEFAULT 0,
            valor3 TYPE i DEFAULT 0.

* Declara variáveis
DATA:   minimo    TYPE decfloat34,
        maximo    TYPE decfloat34,
        val1      TYPE decfloat34,
        val2      TYPE decfloat34,
        val3      TYPE decfloat34,
        txt       TYPE string.

* Inicio da seleção
START-OF-SELECTION.

* Verifica os valores minimos e máximos
minimo =  nmin( val1 = valor1 val2 = valor2 val3 = valor3 ).
maximo =  nmax( val1 = valor1 val2 = valor2 val3 = valor3 ).

* Cria texto da menssagem
 txt = |{ txt } o valor minimo é: { minimo } e o valor máximo é: { maximo } |.

* Exibe menssagem de informação
 MESSAGE txt TYPE 'I'.

* Exibe menssagem de erro (valida campos)
AT SELECTION-SCREEN.
  IF valor1 = 0 or valor2 = 0 or valor3 = 0.
    MESSAGE 'Você deve entrar com um valor diferente de zero' TYPE 'E'.
  ENDIF.

Em um navegador da web, um bookmark” é uma URL salva como um site favorito, o famoso atalho para alguns browser CTRL + D. Se você marcar um javascript: URL, você está salvando um pequeno script, conhecido como um bookmarklet.

Um bookmarklet é um mini programa que pode ser facilmente lançado de menus e barra de ferramentas do navegador. O código em um bookmarklet funciona como se fosse um script na página e pode consultar e definir o conteúdo do documento, apresentação e comportamento. Enquanto um bookmarklet não retorna um valor, ele pode operar em qualquer documento que é exibido sem substituir esse documento com novos conteúdos.

Um link como este é útil quando codificado em uma página que está em desenvolvimento, mas torna-se muito mais útil quando armazenada como um favorito que pode ser executado em qualquer página.

Bookmarklets - Javascript

Bookmarklets – Javascript

Exemplo:

Neste exemplo escrevemos um código javascript utilizando o protocolo javascript:URL, note que esta URL JavaScript está escrita em várias linhas, mas o interpretador HTML a trata como uma única linha. Além disso, lembre-se que o código é parte de um atributo HTML em aspas simples, certifique-se que seu código não contenha nenhuma aspas simples. Tente salvar este link como um favorito e verá que ele poderá ser executado em qualquer pagina.

Html / Javascript

<!DOCTYPE html>
<html> 
<head>
   <title>Desenvolvimento Aberto</title>
</head>
 
<body>
 
<h2>Desenvolvimento Aberto</h2>
<h3>Javascript - Bookmarklets </h3>

<p>Entre com numeros e operadores para efetuar calculos </p>

<a href='javascript:
         var val = "";
         var res = "";         
         do {              
              val = prompt("Calculo: " + val + "\n" + res + "\n", val);
              try { res = "Resultado: " + eval(val); }
              catch(ex) { res = ex; } 
            } 
         while(val); 
         void 0;'>Calculadora</a>

<pre>
<script>
// sem script no documento html
</script>
</pre>
 
</body> 
</html>

O Javascript pode ser utilizado de vários modos no lado cliente, um destes modos é utilizando o especificador do protocolo Javascript em uma URL, o interpretador identifica o protocolo e especifica que a URL contem código Javascript e automaticamente o executa. O código deve estar contido em uma única linha, por isto é necessário que o código seja separado pelo caractere ponto e virgula no final de cada instrução.

O código pode ser inserido dentro de uma tag HTML do tipo <a  href>, deste modo pode-se dizer que você pode usar código Javascript em qualquer lugar que você usaria uma URL comum.

Protocolo - javascript:

Protocolo – javascript:

Exemplo:

Neste exemplo utilizamos a tag  HREF para criar um link e substituímos a URL por um código javascript. Note que o Browser se comporta de um modo diferente nos links abaixo.

Html / Javascript

<!DOCTYPE html>
<html>
<head>
   <title>Desenvolvimento Aberto</title>
</head>

<body>

<h2>Desenvolvimento Aberto</h2>
<h3>Javascript - Javascript em URLs </h3>

<p><a href="javascript:new Date().toLocaleTimeString();">
Mostra hora atual sobrescrevendo o documento</a></p>

<p><a href="javascript:alert(new Date().toLocaleTimeString());">
Mostra hora sem sobrescrever o documento</a></p>

<p><a href="javascript:void window.open('about:blank');">Abre Janela Vazia</a></p>

<pre>
<script>
// sem script no documento html

</script>
</pre>

</body>
</html>

A linguagem de programação Ruby permite que você valide os campos do modelo a fim de criar algumas restrições, por exemplo na hora de inserir um novo registro no banco de dados o desenvolvedor pode prevenir campos em branco ou contendo uma sequencia de caracteres menores que a estipulada pelo programador. A instrução Validates permite além destas mencionadas, vários outros tipos de validações dos campos definidos no modelo.

Ruby on Rails - Validações

Ruby on Rails – Validações

Validando campos do Modelo

Para validar os campos do modelo precisamos usar o arquivo de modelo gerado na hora da criação do projeto, chamado empresa.rb. Também precisamos modificar o arquivo html responsável por inserir um novo registro no banco de dados, este arquivo se chama new.html.erb:

Arquivos Model e View

Arquivos Model e View

Exemplo:

Neste exemplo modificamos o arquivo de modelo para criar uma validação dos campos a serem inseridos no arquivo de visão responsável por criar um novo registro no banco de dados, utilizando o comando validate.

Ruby

Model – empresa.rb

class Empresa < ActiveRecord::Base

   # Escreve validação do Moldelo (Model) 

   # Validações:  codigo e nome obrigatório
   validates :codigo, :nome, :presence => true

   # Validações:  tamanho minimo do campo, cinco caracteres
   validates :nome, :length => { :minimum => 5}

   # Validações:  nome unico no banco de dados
   validates :nome, :uniqueness => true

end

View – new.html.erb

<h2> Adicionar nova Empresa </h2>

<%= form_for @empresa do |cad|%>

    <% if @empresa.errors.any?  %>
       <h3>Erros:  </h3>
       <ul>
       	  <% @empresa.errors.full_messages.each do |menssagem| %>
       	  <li> <%= menssagem %> </li>
       	  <% end %>
       </ul>
    <% end %>

	<p>
		<%= cad.label :codigo%> <br />
		<%= cad.text_field :codigo%>
	</p>

	<p>
		<%= cad.label :nome%> <br />
		<%= cad.text_field :nome%>
	</p>

	<p>
		<%= cad.label :site%> <br />
		<%= cad.text_field :site%>
	</p>

	<p>
		<%= cad.label :projeto%> <br />
		<%= cad.text_field :projeto%>
	</p>

	<p>
		<%= cad.label :tipo%> <br />
		<%= cad.text_field :tipo%>
	</p>

	<p>
		<%= cad.label :linguagem%> <br />
		<%= cad.text_field :linguagem%>
	</p>

	<p>
		<%= cad.label :descricao%> <br />
		<%= cad.text_field :descricao%>
	</p>

	<p> <%= cad.submit "Adicionar dados" %></p>

<% end %>

Database – Cursor – Oracle – Python

Publicado: 7 de julho de 2014 em Python

A linguagem de banco de dados SQL permite que você retorne um resultado de dados completo de uma única vez, mas as vezes precisamos manipular este resultado em tempo de execução, uma linha de cada vez, deste modo podemos utilizar um recurso chamado Cursor para realizar este procedimento.

Basicamente todos os bancos de dados possuem este recurso, entretanto cada um deles possui também suas peculiaridades e funcionalidades diferenciadas, mas no geral funcionam da seguinte maneira: a instrução DECLARE CURSOR define os atributos de um cursor do servidor SQL, como o seu comportamento de rolagem e a consulta usada para construir o conjunto de resultados no qual o cursor funciona. A instrução OPEN popula o conjunto de resultados e FETCH retorna uma linha do conjunto de resultados. A instrução CLOSE libera o conjunto de resultados atual associado ao cursor. A declaração DEALLOCATE libera os recursos usados ​​pelo cursor.

Python - Oracle - Cursor

Python – Oracle – Cursor

Preparando o Banco de dados

Utilizaremos uma modelagem básica contendo três tabelas básicas sem chave primaria, sem chave estrangeira, permitido assim que nosso exemplo possua dados duplicados,  basicamente utilizaremos apenas a logica relacional para brincar com os dados utilizando uma Stored procedure contendo um cursor.

Relacionamentos

A tabela de FUNCIONARIOS contem dados de cadastro e o salario de cada funcionário, a tabela DESCONTO possui os valores de porcentagens de desconto para cada funcionário, e a tabela SALARIO contem os lançamentos de salários e deve ser alimentada mês a mês para cada funcionário.  Utilize os Scripts abaixo para criar as tabelas e os dados iniciais.

Algo Extremamente Útil  Sobre Cursores

Cursores são mais rápidos do que os looping efetuados dentro da linguagem de programação de sua preferencia, funcionam basicamente da mesma maneira, porem residem no motor do banco de dados dentro de uma Stored Procedure.

Os Cursores utilizam memoria e também necessitam de um laço para rolar registro a registro do resultado de dados e manipula-lo, é de extrema importância que você utilize cursores somente quando necessário. Caso queira que seu programa tenha uma ótima performance, antes de optar por um cursor é necessário saber que os motores dos bancos de dados são projetados para manipular dados em massa, manipulação de dados registro a registro devem ser utilizados somente quando não houver possibilidade de usar instruções SQL, para ilustrar esta explicação vamos escrever um script SQL que executa o mesmo procedimento do cursor utilizando apenas um acesso ao banco de dados, você encontra este script logo após o código Python.

Exemplo:

Neste exemplo criamos um cursor que reside dentro de uma Stored Procedure, calcula o desconto do salario dos funcionários e insere os valores líquidos em uma tabela de lançamento, após efetuar o procedimento que seria mensal, uma query exibe o relatório em uma grade de dados.

Cursor

O cursor seleciona dados da tabela de FUNCIONARIOS e da tabela de DESCONTO, através de um looping, alimenta os dados relevantes dentro de variáveis e insere os lançamentos na tabela de SALARIO efetuando o calculo do desconto.

SQL

Oracle

create table Funcionarios
(     

    ID_Funcionario  NUMBER(5),
    Nome            VARCHAR2(30),
    Sobrenome       VARCHAR2(70),
    Cargo           VARCHAR2(30),
    Salario         NUMBER(9,2)

);

Insert into FUNCIONARIOS values (1,'Steve','Gates','Programador',2550.56);
Insert into FUNCIONARIOS values (2,'Bill','Jobs','Diretor',5143.71);

-- Cria tabela com a porcentagem de descontos
Create table DESCONTO (
  ID_FUNCIONARIO NUMBER,
  PORCENTAGEM NUMBER(9,2));

-- Insere porcentagem por  funcionario
Insert Into DESCONTO Values (1, 5 );
Insert Into DESCONTO Values (2, 8 );

-- Cria tabela de lançamentos de descontos
Create table SALARIO (
  ID_FUNCIONARIO NUMBER,
  DATA_LANC  DATE,
  VDESCONTO NUMBER(9,2));

-- Lista lançamentos por funcionario
select * from salario;

-- Desenvolvimento Aberto - Cursor explicito
create or replace Procedure CalculoDesconto  is
-- Declara cursor
Cursor calculo is
  Select A.ID_FUNCIONARIO, A.SALARIO, B.PORCENTAGEM
  from FUNCIONARIOS A, DESCONTO B
  Where
  A.ID_FUNCIONARIO = B.ID_FUNCIONARIO;

  -- Declara variáveis
  pID NUMBER;
  pSalario NUMBER(9,2);
  pPorcentagem NUMBER(9,2);

  -- Abre cursor
  begin
    open calculo;
      -- Cria laço
      loop
        -- Alimenta variáveis
        fetch calculo into pID, pSalario, pPorcentagem;
        EXIT WHEN calculo%NOTFOUND;

        -- Insere valores da tabela
        Insert into SALARIO
        values  (
                 pID,
                 SYSDATE,
                 (pSalario * pPorcentagem)/100);

      end loop;
    -- Fecha cursor
    close calculo;  

  end;

Python

#!/usr/bin/env python
# -*- coding: latin-1 -*-
# Desenvolvimento Aberto
# CursorOracle.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      

# Cria conexão Oracle
def conectarORA():
    sconexao = "user/passw0rd@localhost/XE"
    try:
        con = cx_Oracle.connect(sconexao)
    except ValueError:
        tkMessageBox.showinfo(title="Menssagem", message="Erro de Conexão", parent=janela)
    return con

# Cria conexão
con = conectarORA()

# Executa e retorna SQL
def retornaTabelaORA(sql, con):
    cursor = con.cursor()
    cursor.execute(sql)
    return cursor

# Inicializa Grade
dados = []
colLabels  = []
rowLabels = ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10")   

# Executa Stored Procedure
proc = con.cursor()
proc.callproc("CALCULODESCONTO")
con.commit()

# 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"

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,60), size=(850,200))
        tableBase = GenericTable(dados, rowLabels, colLabels)
        self.SetTable(tableBase)                   

# Cria formulario
class TestFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, -1, "Desenvolvimento Aberto - Cursor - Python", size=(900, 350))
        panel = wx.Panel(self, wx.ID_ANY)
        label=wx.StaticText(panel, -1, label='Oracle Database - Stored Procedure -> Cursor', pos=(300,20))
        grid = SimpleGrid(panel)

# Inicializa a aplicação
app = wx.PySimpleApp()
frame = TestFrame(None)
frame.Show(True)
app.MainLoop()

SQL – A instrução a seguir, substitui o cursor em um único acesso. (Oracle)

-- Este script SQL efetua o mesmo procedimento do cursor
-- utilizando apenas um acesso ao banco de dados.
-- é importante projetar seu acesso a dados para obter alta performance
-- você deve utilizar cursores apenas quando realmente for necessário.
--

Insert into SALARIO
Select A.ID_FUNCIONARIO,SYSDATE, ((A.SALARIO * B.PORCENTAGEM)/100)
  from FUNCIONARIOS A, DESCONTO B
  Where
  A.ID_FUNCIONARIO = B.ID_FUNCIONARIO;

 

 

A linguagem de banco de dados SQL permite que você retorne um resultado de dados completo de uma única vez, mas as vezes precisamos manipular este resultado em tempo de execução, uma linha de cada vez, deste modo podemos utilizar um recurso chamado Cursor para realizar este procedimento.

Basicamente todos os bancos de dados possuem este recurso, entretanto cada um deles possui também suas peculiaridades e funcionalidades diferenciadas, mas no geral funcionam da seguinte maneira: a instrução DECLARE CURSOR define os atributos de um cursor do servidor SQL, como o seu comportamento de rolagem e a consulta usada para construir o conjunto de resultados no qual o cursor funciona. A instrução OPEN popula o conjunto de resultados e FETCH retorna uma linha do conjunto de resultados. A instrução CLOSE libera o conjunto de resultados atual associado ao cursor. A declaração DEALLOCATE libera os recursos usados ​​pelo cursor.

Cursor - C++

Cursor – C++

Preparando o Banco de dados

Utilizaremos uma modelagem básica contendo três tabelas básicas sem chave primaria, sem chave estrangeira, permitido assim que nosso exemplo possua dados duplicados,  basicamente utilizaremos apenas a logica relacional para brincar com os dados utilizando uma Stored procedure contendo um cursor.

Relacionamentos

A tabela de FUNCIONARIOS contem dados de cadastro e o salario de cada funcionário, a tabela DESCONTO possui os valores de porcentagens de desconto para cada funcionário, e a tabela SALARIO contem os lançamentos de salários e deve ser alimentada mês a mês para cada funcionário.  Utilize os Scripts abaixo para criar as tabelas e os dados iniciais.

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, 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:

Visual Studio - DesignTime - Cpp

Visual Studio – Design Time – Cpp

 

Algo Extremamente Útil  Sobre Cursores

Cursores são mais rápidos do que os looping efetuados dentro da linguagem de programação de sua preferencia, funcionam basicamente da mesma maneira, porem residem no motor do banco de dados dentro de uma Stored Procedure.

Os Cursores utilizam memoria e também necessitam de um laço para rolar registro a registro do resultado de dados e manipula-lo, é de extrema importância que você utilize cursores somente quando necessário. Caso queira que seu programa tenha uma ótima performance, antes de optar por um cursor é necessário saber que os motores dos bancos de dados são projetados para manipular dados em massa, manipulação de dados registro a registro deve ser utilizada somente quando não houver possibilidade de usar instruções SQL, para ilustrar esta explicação vamos escrever um script SQL que executa o mesmo procedimento do cursor utilizando apenas um acesso ao banco de dados, você encontra este script logo após o código C++.

Exemplo:

Neste exemplo criamos um cursor que reside dentro de uma Stored Procedure, calcula o desconto do salario dos funcionários e insere os valores líquidos em uma tabela de lançamento, após efetuar o procedimento que seria mensal, uma query exibe o relatório em uma grade de dados.

Cursor

O cursor seleciona dados da tabela de FUNCIONARIOS e da tabela de DESCONTO, através de um looping, alimenta os dados relevantes dentro de variáveis e insere os lançamentos na tabela de SALARIO efetuando o calculo do desconto.

SQL

Oracle

create table Funcionarios
(     

    ID_Funcionario  NUMBER(5),
    Nome            VARCHAR2(30),
    Sobrenome       VARCHAR2(70),
    Cargo           VARCHAR2(30),
    Salario         NUMBER(9,2)

);

Insert into FUNCIONARIOS values (1,'Steve','Gates','Programador',2550.56);
Insert into FUNCIONARIOS values (2,'Bill','Jobs','Diretor',5143.71);

-- Cria tabela com a porcentagem de descontos
Create table DESCONTO (
  ID_FUNCIONARIO NUMBER,
  PORCENTAGEM NUMBER(9,2));

-- Insere porcentagem por  funcionario
Insert Into DESCONTO Values (1, 5 );
Insert Into DESCONTO Values (2, 8 );

-- Cria tabela de lançamentos de descontos
Create table SALARIO (
  ID_FUNCIONARIO NUMBER,
  DATA_LANC  DATE,
  VDESCONTO NUMBER(9,2));

-- Lista lançamentos por funcionario
select * from salario;

-- Desenvolvimento Aberto - Cursor explicito
create or replace Procedure CalculoDesconto  is
-- Declara cursor
Cursor calculo is
  Select A.ID_FUNCIONARIO, A.SALARIO, B.PORCENTAGEM
  from FUNCIONARIOS A, DESCONTO B
  Where
  A.ID_FUNCIONARIO = B.ID_FUNCIONARIO;

  -- Declara variáveis
  pID NUMBER;
  pSalario NUMBER(9,2);
  pPorcentagem NUMBER(9,2);

  -- Abre cursor
  begin
    open calculo;
      -- Cria laço
      loop
        -- Alimenta variáveis
        fetch calculo into pID, pSalario, pPorcentagem;
        EXIT WHEN calculo%NOTFOUND;

        -- Insere valores da tabela
        Insert into SALARIO
        values  (
                 pID,
                 SYSDATE,
                 (pSalario * pPorcentagem)/100);

      end loop;
    -- Fecha cursor
    close calculo;  

  end;

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);

-- Cria tabela com a porcentagem de descontos
Create table DESCONTO (
  ID_FUNCIONARIO INTEGER,
  PORCENTAGEM DECIMAL(9,2));

-- Insere porcentagem por  funcionario
Insert Into DESCONTO Values (1, 5 );
Insert Into DESCONTO Values (2, 8 );

-- Cria tabela de lançamentos de descontos
Create table SALARIO (
  ID_FUNCIONARIO INTEGER,
  DATA_LANC  DATE,
  VDESCONTO DECIMAL(9,2));

-- Lista lançamentos por funcionario
select * from desconto;

-- Desencolcimento Aberto - Cursor explicito
CREATE PROCEDURE calculodesconto ()
	DYNAMIC RESULT SETS 1	

P1: BEGIN

	-- Declara variáveis
	 DECLARE pID INTEGER;
	 DECLARE pSalario DECIMAL(9,2);
	 DECLARE pPorcentagem DECIMAL(9,2);
	 DECLARE eof SMALLINT DEFAULT 0;

	-- Declara cursor
	DECLARE calculo CURSOR WITH RETURN for
	Select A.ID_FUNCIONARIO, A.SALARIO, B.PORCENTAGEM
    from FUNCIONARIOS A, DESCONTO B
    Where
    A.ID_FUNCIONARIO = B.ID_FUNCIONARIO;

   -- Declara handler para final de arquivo
   DECLARE CONTINUE HANDLER FOR NOT FOUND SET eof = 1;

	-- Abre cursor
	OPEN calculo;

	-- Cria label e executa looping
	fim:
	LOOP
	  -- Alimenta valores nas variáveis
	  FETCH calculo into pID, pSalario, pPorcentagem;
	  	  IF eof <> 0 THEN LEAVE fim;
	      END IF;	  

	      -- Insere dados na tabela
	      Insert into SALARIO values (
	        pID,
	        Current date,
	        (pSalario * pPorcentagem)/100);

	END LOOP fim;
	CLOSE calculo;
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);

-- Cria tabela com a porcentagem de descontos
Create table DESCONTO (
  ID_FUNCIONARIO INT,
  PORCENTAGEM DECIMAL(9,2));

-- Insere porcentagem por  funcionario
Insert Into DESCONTO Values (1, 5 );
Insert Into DESCONTO Values (2, 8 );

-- Cria tabela de lançamentos de descontos
Create table SALARIO (
  ID_FUNCIONARIO INT,
  DATA_LANC  DATE,
  VDESCONTO DECIMAL(9,2));

-- Lista lançamentos por funcionario
select * from salario;

-- Desenvolvimento Aberto - cursor explicito
Create Procedure CALCULODESCONTO
AS
BEGIN
-- Declara Variáveis
DECLARE @pID INT,
        @pSalario DECIMAL(9,2),
		@pPorcentagem DECIMAL(9,2);

-- Declara cursor
DECLARE calculo CURSOR FOR
   Select A.ID_FUNCIONARIO, A.SALARIO, B.PORCENTAGEM
   from FUNCIONARIOS A,   DESCONTO B
   Where
   A.ID_FUNCIONARIO = B.ID_FUNCIONARIO;

   -- Abre cursor
   Open calculo;

   -- Alimenta -
   FETCH NEXT FROM calculo INTO @pID, @pSalario, @pPorcentagem;

   While @@FETCH_STATUS = 0
   BEGIN
      Insert into SALARIO values (
	        @pID,
	        GETDATE(),
	        (@pSalario * @pPorcentagem)/100);

      FETCH NEXT FROM calculo INTO @pID, @pSalario, @pPorcentagem;
   END

   -- Fecha conteudo do cursor
   Close calculo;
   -- Desaloca cursor da memória
   Deallocate calculo;
END

C++
Classe: CAcessocpp

Arquivo .h

// Classe gerada automaticamente
// AcessocppDlg.h : header file
//

#pragma once

// Inclui classe de banco de dados MFC
#include "afxdb.h"
#include "afxcmn.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;
};

Arquivo .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);
}

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 - ODBC - (DNS,user,pass)
	if (bancodedados == "oracle")
	{
		conectarDB(L"OracleXE", L"user", L"p@55w0rd");
	}

	if (bancodedados == "db2")
	{
		conectarDB(L"IBMDB2", L"user", L"p@55w0rd");
	}

	if (bancodedados == "mssql")
	{
		conectarDB(L"MSSQLSERVER", L"user", L"p@55w0rd");
	}

	// Cria um set de dados
	CRecordset  dados(&db);

	if (bancodedados == "mssql")
	{
		db.ExecuteSQL(_T("EXEC CALCULODESCONTO"));
	}
	else
	{
		db.ExecuteSQL(_T("CALL CALCULODESCONTO();"));
	}

	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);
}

SQL – A instrução a seguir, substitui o cursor em um único acesso. (Oracle)

-- Este script SQL efetua o mesmo procedimento do cursor
-- utilizando apenas um acesso ao banco de dados.
-- é importante projetar seu acesso a dados para obter alta performance
-- você deve utilizar cursores apenas quando realmente for necessário.
--
-- Para os outros bancos substitua o campo SYSDATE
-- Para DB2 use Current date
-- Para MSSQL use GETDATE() 

Insert into SALARIO
Select A.ID_FUNCIONARIO,SYSDATE, ((A.SALARIO * B.PORCENTAGEM)/100)
  from FUNCIONARIOS A, DESCONTO B
  Where
  A.ID_FUNCIONARIO = B.ID_FUNCIONARIO;

 

 

A linguagem de banco de dados SQL permite que você retorne um resultado de dados completo de uma única vez, mas as vezes precisamos manipular este resultado em tempo de execução, uma linha de cada vez, deste modo podemos utilizar um recurso chamado Cursor para realizar este procedimento.

Basicamente todos os bancos de dados possuem este recurso, entretanto cada um deles possui também suas peculiaridades e funcionalidades diferenciadas, mas no geral funcionam da seguinte maneira: a instrução DECLARE CURSOR define os atributos de um cursor do servidor SQL, como o seu comportamento de rolagem e a consulta usada para construir o conjunto de resultados no qual o cursor funciona. A instrução OPEN popula o conjunto de resultados e FETCH retorna uma linha do conjunto de resultados. A instrução CLOSE libera o conjunto de resultados atual associado ao cursor. A declaração DEALLOCATE libera os recursos usados ​​pelo cursor.

Cursor - C#

Cursor – C#

Preparando o Banco de dados

Utilizaremos uma modelagem básica contendo três tabelas básicas sem chave primaria, sem chave estrangeira, permitido assim que nosso exemplo possua dados duplicados,  basicamente utilizaremos apenas a logica relacional para brincar com os dados utilizando uma Stored procedure contendo um cursor.

Relacionamentos

A tabela de FUNCIONARIOS contem dados de cadastro e o salario de cada funcionário, a tabela DESCONTO possui os valores de porcentagens de desconto para cada funcionário, e a tabela SALARIO contem os lançamentos de salários e deve ser alimentada mês a mês para cada funcionário.  Utilize os Scripts abaixo para criar as tabelas e os dados iniciais.

Visual Studio

Para efetuar as conexões com os diferentes bancos de dados você precisa primeiro configurar seu projeto com os assemblers ADO.NET  necessários e depois você pode criar um design com 3 componentes RadioButton, um componente Button e um componente DataGridView use a figura abaixo para referencia:

Visual Studio - Design Time

Visual Studio – Design Time

 

Algo Extremamente Útil  Sobre Cursores

Cursores são mais rápidos do que os looping efetuados dentro da linguagem de programação de sua preferencia, funcionam basicamente da mesma maneira, porem residem no motor do banco de dados dentro de uma Stored Procedure.

Os Cursores utilizam memoria e também necessitam de um laço para rolar registro a registro do resultado de dados e manipula-lo, é de extrema importância que você utilize cursores somente quando necessário. Caso queira que seu programa tenha uma ótima performance, antes de optar por um cursor é necessário saber que os motores dos bancos de dados são projetados para manipular dados em massa, manipulação de dados registro a registro deve ser utilizada somente quando não houver possibilidade de usar instruções SQL, para ilustrar esta explicação vamos escrever um script SQL que executa o mesmo procedimento do cursor utilizando apenas um acesso ao banco de dados, você encontra este script logo após o código C#.

Exemplo:

Neste exemplo criamos um cursor que reside dentro de uma Stored Procedure, calcula o desconto do salario dos funcionários e insere os valores líquidos em uma tabela de lançamento, após efetuar o procedimento que seria mensal, uma query exibe o relatório em uma grade de dados.

Cursor

O cursor seleciona dados da tabela de FUNCIONARIOS e da tabela de DESCONTO, através de um looping, alimenta os dados relevantes dentro de variáveis e insere os lançamentos na tabela de SALARIO efetuando o calculo do desconto.

SQL

Oracle

create table Funcionarios
(     

    ID_Funcionario  NUMBER(5),
    Nome            VARCHAR2(30),
    Sobrenome       VARCHAR2(70),
    Cargo           VARCHAR2(30),
    Salario         NUMBER(9,2)

);

Insert into FUNCIONARIOS values (1,'Steve','Gates','Programador',2550.56);
Insert into FUNCIONARIOS values (2,'Bill','Jobs','Diretor',5143.71);

-- Cria tabela com a porcentagem de descontos
Create table DESCONTO (
  ID_FUNCIONARIO NUMBER,
  PORCENTAGEM NUMBER(9,2));

-- Insere porcentagem por  funcionario
Insert Into DESCONTO Values (1, 5 );
Insert Into DESCONTO Values (2, 8 );

-- Cria tabela de lançamentos de descontos
Create table SALARIO (
  ID_FUNCIONARIO NUMBER,
  DATA_LANC  DATE,
  VDESCONTO NUMBER(9,2));

-- Lista lançamentos por funcionario
select * from salario;

-- Desenvolvimento Aberto - Cursor explicito
create or replace Procedure CalculoDesconto  is
-- Declara cursor
Cursor calculo is
  Select A.ID_FUNCIONARIO, A.SALARIO, B.PORCENTAGEM
  from FUNCIONARIOS A, DESCONTO B
  Where
  A.ID_FUNCIONARIO = B.ID_FUNCIONARIO;

  -- Declara variáveis
  pID NUMBER;
  pSalario NUMBER(9,2);
  pPorcentagem NUMBER(9,2);

  -- Abre cursor
  begin
    open calculo;
      -- Cria laço
      loop
        -- Alimenta variáveis
        fetch calculo into pID, pSalario, pPorcentagem;
        EXIT WHEN calculo%NOTFOUND;

        -- Insere valores da tabela
        Insert into SALARIO
        values  (
                 pID,
                 SYSDATE,
                 (pSalario * pPorcentagem)/100);

      end loop;
    -- Fecha cursor
    close calculo;  

  end;

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);

-- Cria tabela com a porcentagem de descontos
Create table DESCONTO (
  ID_FUNCIONARIO INTEGER,
  PORCENTAGEM DECIMAL(9,2));

-- Insere porcentagem por  funcionario
Insert Into DESCONTO Values (1, 5 );
Insert Into DESCONTO Values (2, 8 );

-- Cria tabela de lançamentos de descontos
Create table SALARIO (
  ID_FUNCIONARIO INTEGER,
  DATA_LANC  DATE,
  VDESCONTO DECIMAL(9,2));

-- Lista lançamentos por funcionario
select * from desconto;

-- Desencolcimento Aberto - Cursor explicito
CREATE PROCEDURE calculodesconto ()
	DYNAMIC RESULT SETS 1	

P1: BEGIN

	-- Declara variáveis
	 DECLARE pID INTEGER;
	 DECLARE pSalario DECIMAL(9,2);
	 DECLARE pPorcentagem DECIMAL(9,2);
	 DECLARE eof SMALLINT DEFAULT 0;

	-- Declara cursor
	DECLARE calculo CURSOR WITH RETURN for
	Select A.ID_FUNCIONARIO, A.SALARIO, B.PORCENTAGEM
    from FUNCIONARIOS A, DESCONTO B
    Where
    A.ID_FUNCIONARIO = B.ID_FUNCIONARIO;

   -- Declara handler para final de arquivo
   DECLARE CONTINUE HANDLER FOR NOT FOUND SET eof = 1;

	-- Abre cursor
	OPEN calculo;

	-- Cria label e executa looping
	fim:
	LOOP
	  -- Alimenta valores nas variáveis
	  FETCH calculo into pID, pSalario, pPorcentagem;
	  	  IF eof &lt;&gt; 0 THEN LEAVE fim;
	      END IF;	  

	      -- Insere dados na tabela
	      Insert into SALARIO values (
	        pID,
	        Current date,
	        (pSalario * pPorcentagem)/100);

	END LOOP fim;
	CLOSE calculo;
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);

-- Cria tabela com a porcentagem de descontos
Create table DESCONTO (
  ID_FUNCIONARIO INT,
  PORCENTAGEM DECIMAL(9,2));

-- Insere porcentagem por  funcionario
Insert Into DESCONTO Values (1, 5 );
Insert Into DESCONTO Values (2, 8 );

-- Cria tabela de lançamentos de descontos
Create table SALARIO (
  ID_FUNCIONARIO INT,
  DATA_LANC  DATE,
  VDESCONTO DECIMAL(9,2));

-- Lista lançamentos por funcionario
select * from salario;

-- Desenvolvimento Aberto - cursor explicito
Create Procedure CALCULODESCONTO
AS
BEGIN
-- Declara Variáveis
DECLARE @pID INT,
        @pSalario DECIMAL(9,2),
		@pPorcentagem DECIMAL(9,2);

-- Declara cursor
DECLARE calculo CURSOR FOR
   Select A.ID_FUNCIONARIO, A.SALARIO, B.PORCENTAGEM
   from FUNCIONARIOS A,   DESCONTO B
   Where
   A.ID_FUNCIONARIO = B.ID_FUNCIONARIO;

   -- Abre cursor
   Open calculo;

   -- Alimenta -
   FETCH NEXT FROM calculo INTO @pID, @pSalario, @pPorcentagem;

   While @@FETCH_STATUS = 0
   BEGIN
      Insert into SALARIO values (
	        @pID,
	        GETDATE(),
	        (@pSalario * @pPorcentagem)/100);

      FETCH NEXT FROM calculo INTO @pID, @pSalario, @pPorcentagem;
   END

   -- Fecha conteudo do cursor
   Close calculo;
   -- Desaloca cursor da memória
   Deallocate calculo;
END

C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Data.SqlClient;
using Oracle.DataAccess.Client;
using IBM.Data.DB2; 

namespace Acesso
{
    public partial class Dados : Form
    {
        private static OracleConnection connORA; // ODAC 12c
        private static DB2Connection connDB2;   // IBM Data Server Provider
        private static SqlConnection connMSSQL; // ADO .NET

        DataTable dataTable;

        string sql;
        static string bancodedados;

        public Dados()
        {
            InitializeComponent();
        }

        // Cria métodos de conexão
        // Você pode optar por um unico método se usar apenas ADO.NET
        // Neste caso usamos os drivers nativos de conexão de cada banco de dados.
        // Todos os drivers são baseados em ADO .NET
        public void conexaoODAC(string Username, string Password, string Datasource)
        {
            try
            {
                // String de Conexao
                string connectionString =

                    // Usuario
                    "User Id=" + Username +

                    // Senha
                    ";Password=" + Password +

                    // TNSnames
                    ";Data Source=" + Datasource;

                //Conecta ao datasource usando a conexão Oracle
                 connORA = new OracleConnection(connectionString);

                //Abre a conexão com o banco de dados
                connORA.Open();

            }
            // Retorna erro
            catch (Exception ex)
            {
                // Mostra menssagem de erro
                MessageBox.Show(ex.ToString());

            }
        }

        void conexaoDB2(string Server, string Database,
                                    string Username, string Password, string Timeout)
        {
            try
            {
                // String de Conexao
                string connectionString =

                    // Servidor
                    "Server=" + Server +

                    // Banco de dados
                    ";Database=" + Database +

                    // Usuario
                    ";UID=" + Username +

                    // Senha
                    ";PWD=" + Password +

                    // TNSnames
                    ";Connect Timeout=" + Timeout;

                //Conecta ao datasource usando a conexão DB2
                connDB2 = new DB2Connection(connectionString);

                //Abre a conexão com o banco de dados
                connDB2.Open();                

            }
            // Retorna erro
            catch (Exception ex)
            {
                // Mostra menssagem de erro
                MessageBox.Show(ex.ToString());

            }
        }

         public void conexaoMSSQL(string Server, string Database,
                                     string Username, string Password, string Timeout)
         {
             try
             {
                 // String de Conexao
                 string connectionString =

                     // Servidor
                     "Data Source=" + Server +

                     // Banco de dados
                     ";Initial Catalog=" + Database +

                     // Usuario
                     ";User ID =" + Username +

                     // Senha
                     ";Password=" + Password +

                     // TNSnames
                     ";Connect Timeout=" + Timeout;

                 //Conecta ao datasource usando a conexão Padrão
                 connMSSQL = new SqlConnection(connectionString);

                 //Abre a conexão com o banco de dados
                 connMSSQL.Open();

             }
             // Retorna erro
             catch (Exception ex)
             {
                 // Mostra menssagem de erro
                 MessageBox.Show(ex.ToString());

             }
         }

        // Retorna um set de dados
         public  DataTable retornaTabela(string sql)
         {
             // Declara comandos para Stored Procedure
             OracleCommand cmdORA;
             DB2Command cmdDB2;
             SqlCommand cmdSQL;

             // Cria instância da classe
             Dados acesso = new Dados();

             // Define banco de dados

             if (bancodedados == "oracle")
             {
                 // Efetua Login no banco de dados
                 acesso.conexaoODAC("user", "p@55w0rd", "XE");

                 // Define a instrução SQL e a conexão
                 cmdORA = new OracleCommand("CalculoDesconto", connORA);

                 // Define tipo como Stored procedure
                 cmdORA.CommandType = CommandType.StoredProcedure;

                 // Executa query
                 cmdORA.ExecuteNonQuery();

                 // Cria comandos para retornar dados para exibir a grade de dados
                 OracleCommand oracmd = new OracleCommand(sql, connORA);
                 OracleDataReader orareader = oracmd.ExecuteReader();
                 dataTable = new DataTable();
                 dataTable.Load(orareader);
             }

             if (bancodedados == "db2")
             {
                 acesso.conexaoDB2("localhost", "DEVA", "user", "p@55w0rd", "40");

                 cmdDB2 = new DB2Command("CalculoDesconto", connDB2);

                 cmdDB2.CommandType = CommandType.StoredProcedure;

                 cmdDB2.ExecuteNonQuery();

                 DB2Command db2cmd = new DB2Command(sql);
                 db2cmd.Connection = connDB2;
                 DB2DataReader db2reader = db2cmd.ExecuteReader();
                 dataTable = new DataTable();
                 dataTable.Load(db2reader);                

             }

             if (bancodedados == "mssql")
             {
                 acesso.conexaoMSSQL("localhost", "DevAberto", "user", "p@55w0rd", "");

                 cmdSQL = new SqlCommand("CalculoDesconto", connMSSQL);

                 cmdSQL.CommandType = CommandType.StoredProcedure;

                 cmdSQL.ExecuteNonQuery();

                 SqlCommand mssqlcmd = new SqlCommand(sql);
                 mssqlcmd.Connection = connMSSQL;
                 SqlDataReader mssqlreader = mssqlcmd.ExecuteReader();
                 dataTable = new DataTable();
                 dataTable.Load(mssqlreader);

             }

             return  dataTable;
         }

        // configura programa
        private void Form1_Shown(object sender, EventArgs e)
        {
            radioButton1.Checked = true;
            bancodedados = "oracle";
        }

        // Conecta dados
        private void button1_Click(object sender, EventArgs e)
        {
            // Cria instância da classe
            Dados dados = new Dados();

            // Exibe relatorio de lançamentos
            string 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";

            // Alimenta grid de dados
            dataGridView1.DataSource = dados.retornaTabela(sql);

        }

        // Seleciona banco de dados Oracle
        private void radioButton1_CheckedChanged(object sender, EventArgs e)
        {
            if (radioButton1.Checked)
            {
                bancodedados = "oracle";
            }

        }

        // Seleciona banco de dados IBM DB2
        private void radioButton2_CheckedChanged(object sender, EventArgs e)
        {
            if (radioButton2.Checked)
            {
                bancodedados = "db2";
            }
        }

        // Seleciona banco de dados MSSQL Server
        private void radioButton3_CheckedChanged(object sender, EventArgs e)
        {
            if (radioButton3.Checked)
            {
                bancodedados = "mssql";
            }
        }
    }
}

SQL – A instrução a seguir, substitui o cursor em um único acesso. (Oracle)

-- Este script SQL efetua o mesmo procedimento do cursor
-- utilizando apenas um acesso ao banco de dados.
-- é importante projetar seu acesso a dados para obter alta performance
-- você deve utilizar cursores apenas quando realmente for necessário.
--
-- Para os outros bancos substitua o campo SYSDATE
-- Para DB2 use Current date
-- Para MSSQL use GETDATE() 

Insert into SALARIO
Select A.ID_FUNCIONARIO,SYSDATE, ((A.SALARIO * B.PORCENTAGEM)/100)
  from FUNCIONARIOS A, DESCONTO B
  Where
  A.ID_FUNCIONARIO = B.ID_FUNCIONARIO;

 

 

A linguagem de banco de dados SQL permite que você retorne um resultado de dados completo de uma única vez, mas as vezes precisamos manipular este resultado em tempo de execução, uma linha de cada vez, deste modo podemos utilizar um recurso chamado Cursor para realizar este procedimento.

Basicamente todos os bancos de dados possuem este recurso, entretanto cada um deles possui também suas peculiaridades e funcionalidades diferenciadas, mas no geral funcionam da seguinte maneira: a instrução DECLARE CURSOR define os atributos de um cursor do servidor SQL, como o seu comportamento de rolagem e a consulta usada para construir o conjunto de resultados no qual o cursor funciona. A instrução OPEN popula o conjunto de resultados e FETCH retorna uma linha do conjunto de resultados. A instrução CLOSE libera o conjunto de resultados atual associado ao cursor. A declaração DEALLOCATE libera os recursos usados ​​pelo cursor.

Cursor  - SQL

Cursor – SQL

Preparando o Banco de dados

Utilizaremos uma modelagem básica contendo três tabelas básicas sem chave primaria, sem chave estrangeira, permitido assim que nosso exemplo possua dados duplicados,  basicamente utilizaremos apenas a logica relacional para brincar com os dados utilizando uma Stored procedure contendo um cursor.

Relacionamentos

A tabela de FUNCIONARIOS contem dados de cadastro e o salario de cada funcionário, a tabela DESCONTO possui os valores de porcentagens de desconto para cada funcionário, e a tabela SALARIO contem os lançamentos de salários e deve ser alimentada mês a mês para cada funcionário.  Utilize os Scripts abaixo para criar as tabelas e os dados iniciais.

 

Cursor - IBM Data Studio

Cursor – IBM Data Studio

Algo Extremamente Útil  Sobre Cursores

Cursores são mais rápidos do que os looping efetuados dentro da linguagem de programação de sua preferencia, funcionam basicamente da mesma maneira, porem residem no motor do banco de dados dentro de uma Stored Procedure.

Os Cursores utilizam memoria e também necessitam de um laço para rolar registro a registro do resultado de dados e manipula-lo, é de extrema importância que você utilize cursores somente quando necessário. Caso queira que seu programa tenha uma ótima performance, antes de optar por um cursor é necessário saber que os motores dos bancos de dados são projetados para manipular dados em massa, manipulação de dados registro a registro devem ser utilizados somente quando não houver possibilidade de usar instruções SQL, para ilustrar esta explicação vamos escrever um script SQL que executa o mesmo procedimento do cursor utilizando apenas um acesso ao banco de dados, você encontra este script logo após o código Java.

Exemplo:

Neste exemplo criamos um cursor que reside dentro de uma Stored Procedure, calcula o desconto do salario dos funcionários e insere os valores líquidos em uma tabela de lançamento, após efetuar o procedimento que seria mensal, uma query exibe o relatório em uma grade de dados.

Cursor

O cursor seleciona dados da tabela de FUNCIONARIOS e da tabela de DESCONTO, através de um looping, alimenta os dados relevantes dentro de variáveis e insere os lançamentos na tabela de SALARIO efetuando o calculo do desconto.

SQL

Oracle

create table Funcionarios
(     

    ID_Funcionario  NUMBER(5),
    Nome            VARCHAR2(30),
    Sobrenome       VARCHAR2(70),
    Cargo           VARCHAR2(30),
    Salario         NUMBER(9,2)

);

Insert into FUNCIONARIOS values (1,'Steve','Gates','Programador',2550.56);
Insert into FUNCIONARIOS values (2,'Bill','Jobs','Diretor',5143.71);

-- Cria tabela com a porcentagem de descontos
Create table DESCONTO (
  ID_FUNCIONARIO NUMBER,
  PORCENTAGEM NUMBER(9,2));

-- Insere porcentagem por  funcionario
Insert Into DESCONTO Values (1, 5 );
Insert Into DESCONTO Values (2, 8 );

-- Cria tabela de lançamentos de descontos
Create table SALARIO (
  ID_FUNCIONARIO NUMBER,
  DATA_LANC  DATE,
  VDESCONTO NUMBER(9,2));

-- Lista lançamentos por funcionario
select * from salario;

-- Desenvolvimento Aberto - Cursor explicito
create or replace Procedure CalculoDesconto  is
-- Declara cursor
Cursor calculo is
  Select A.ID_FUNCIONARIO, A.SALARIO, B.PORCENTAGEM
  from FUNCIONARIOS A, DESCONTO B
  Where
  A.ID_FUNCIONARIO = B.ID_FUNCIONARIO;

  -- Declara variáveis
  pID NUMBER;
  pSalario NUMBER(9,2);
  pPorcentagem NUMBER(9,2);

  -- Abre cursor
  begin
    open calculo;
      -- Cria laço
      loop
        -- Alimenta variáveis
        fetch calculo into pID, pSalario, pPorcentagem;
        EXIT WHEN calculo%NOTFOUND;

        -- Insere valores da tabela
        Insert into SALARIO
        values  (
                 pID,
                 SYSDATE,
                 (pSalario * pPorcentagem)/100);

      end loop;
    -- Fecha cursor
    close calculo;  

  end;

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);

-- Cria tabela com a porcentagem de descontos
Create table DESCONTO (
  ID_FUNCIONARIO INTEGER,
  PORCENTAGEM DECIMAL(9,2));

-- Insere porcentagem por  funcionario
Insert Into DESCONTO Values (1, 5 );
Insert Into DESCONTO Values (2, 8 );

-- Cria tabela de lançamentos de descontos
Create table SALARIO (
  ID_FUNCIONARIO INTEGER,
  DATA_LANC  DATE,
  VDESCONTO DECIMAL(9,2));

-- Lista lançamentos por funcionario
select * from desconto;

-- Desencolcimento Aberto - Cursor explicito
CREATE PROCEDURE calculodesconto ()
	DYNAMIC RESULT SETS 1	

P1: BEGIN

	-- Declara variáveis
	 DECLARE pID INTEGER;
	 DECLARE pSalario DECIMAL(9,2);
	 DECLARE pPorcentagem DECIMAL(9,2);
	 DECLARE eof SMALLINT DEFAULT 0;

	-- Declara cursor
	DECLARE calculo CURSOR WITH RETURN for
	Select A.ID_FUNCIONARIO, A.SALARIO, B.PORCENTAGEM
    from FUNCIONARIOS A, DESCONTO B
    Where
    A.ID_FUNCIONARIO = B.ID_FUNCIONARIO;

   -- Declara handler para final de arquivo
   DECLARE CONTINUE HANDLER FOR NOT FOUND SET eof = 1;

	-- Abre cursor
	OPEN calculo;

	-- Cria label e executa looping
	fim:
	LOOP
	  -- Alimenta valores nas variáveis
	  FETCH calculo into pID, pSalario, pPorcentagem;
	  	  IF eof <> 0 THEN LEAVE fim;
	      END IF;	  

	      -- Insere dados na tabela
	      Insert into SALARIO values (
	        pID,
	        Current date,
	        (pSalario * pPorcentagem)/100);

	END LOOP fim;
	CLOSE calculo;
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);

-- Cria tabela com a porcentagem de descontos
Create table DESCONTO (
  ID_FUNCIONARIO INT,
  PORCENTAGEM DECIMAL(9,2));

-- Insere porcentagem por  funcionario
Insert Into DESCONTO Values (1, 5 );
Insert Into DESCONTO Values (2, 8 );

-- Cria tabela de lançamentos de descontos
Create table SALARIO (
  ID_FUNCIONARIO INT,
  DATA_LANC  DATE,
  VDESCONTO DECIMAL(9,2));

-- Lista lançamentos por funcionario
select * from salario;

-- Desenvolvimento Aberto - cursor explicito
Create Procedure CALCULODESCONTO
AS
BEGIN
-- Declara Variáveis
DECLARE @pID INT,
        @pSalario DECIMAL(9,2),
		@pPorcentagem DECIMAL(9,2);

-- Declara cursor
DECLARE calculo CURSOR FOR
   Select A.ID_FUNCIONARIO, A.SALARIO, B.PORCENTAGEM
   from FUNCIONARIOS A,   DESCONTO B
   Where
   A.ID_FUNCIONARIO = B.ID_FUNCIONARIO;

   -- Abre cursor
   Open calculo;

   -- Alimenta -
   FETCH NEXT FROM calculo INTO @pID, @pSalario, @pPorcentagem;

   While @@FETCH_STATUS = 0
   BEGIN
      Insert into SALARIO values (
	        @pID,
	        GETDATE(),
	        (@pSalario * @pPorcentagem)/100);

      FETCH NEXT FROM calculo INTO @pID, @pSalario, @pPorcentagem;
   END

   -- Fecha conteudo do cursor
   Close calculo;
   -- Desaloca cursor da memória
   Deallocate calculo;
END

Java

import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;

public class Cursor implements ActionListener, ItemListener {
	// Cria componentes
	private JTable tabela;
	private JRadioButton banco1;
	private JRadioButton banco2;
	private JRadioButton banco3;
	private JButton botao;

	// Declara objetos de conexão
	private static Connection conn;
	private static Statement query;
	private static String bancodedados;

	public void conectar(String login, String senha) {

		// Verifica strings de conexão

		// ORACLE
		if (bancodedados == "oracle") {
			try {
				// Define Driver de conexão JDBC thin
				Class.forName("oracle.jdbc.driver.OracleDriver");
				conn = DriverManager.getConnection(
						"jdbc:oracle:thin:@localhost:1521:xe", login, senha);

				// Executa pedido SQL
				query = conn.createStatement();

			}

			catch (ClassNotFoundException ex) {
				ex.printStackTrace();
			}

			catch (SQLException ex) {
				ex.printStackTrace();
			}
		}

		// DB2
		if (bancodedados == "db2") {
			try {
				// Define Driver de conexão JDBC
				Class.forName("com.ibm.db2.jcc.DB2Driver");
				conn = DriverManager.getConnection(
						"jdbc:derby:net://localhost:50000/deva", login, senha);

				// Executa pedido SQL
				query = conn.createStatement();
				// JOptionPane.showMessageDialog(menssagem,
				// "Conexão Efetuada com sucesso!");
			}

			catch (ClassNotFoundException ex) {
				ex.printStackTrace();
			}

			catch (SQLException ex) {
				// JOptionPane.showMessageDialog(menssagem, "Erro na conexão!");
				ex.printStackTrace();
			}

		}

		// MICROSOFT SQL SERVER
		if (bancodedados == "mssql") {
			try {
				// Define Driver de conexão JDBC
				String URL = "jdbc:sqlserver://localhost\\SQLEXPRESS:1433;databaseName=devaberto"
						+ ";user=" + login + ";password=" + senha;

				Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
				conn = DriverManager.getConnection(URL);

				// Executa pedido SQL
				query = conn.createStatement();

			}

			catch (ClassNotFoundException ex) {
				ex.printStackTrace();
			}

			catch (SQLException ex) {
				ex.printStackTrace();
			}

		}

	}

	public void executaProcedure() throws SQLException {
		// Cria nova instrução SQL
		CallableStatement proc = null;

		proc = conn.prepareCall("{call CalculoDesconto()}");
		// Executa stored procedure
		proc.execute();

	}

	public ResultSet retornaTabela() throws SQLException {
		// Cria nova instrução SQL
		Statement query;
		query = conn.createStatement();
		String sql;

		// Verfica banco de dados e passa script SQL
		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";

		// Executa Script
		ResultSet dados = query.executeQuery(sql);

		// Retorna set de dados
		return dados;
	}

	// Modelo
	public static DefaultTableModel criaTableModel(ResultSet rs)
			throws SQLException {

		// Cria um modelo de tabela
		ResultSetMetaData metaData = rs.getMetaData();

		// Retorna as colunas
		Vector<String> colunas = new Vector<String>();

		int columnCount = metaData.getColumnCount();

		for (int column = 1; column <= columnCount; column++) {
			colunas.add(metaData.getColumnName(column));
		}

		// Retorna dados
		Vector<Vector<Object>> dados = new Vector<Vector<Object>>();

		while (rs.next()) {
			Vector<Object> vector = new Vector<Object>();

			for (int columnIndex = 1; columnIndex <= columnCount; columnIndex++) {
				vector.add(rs.getObject(columnIndex));
			}
			dados.add(vector);
		}

		return new DefaultTableModel(dados, colunas);

	}

	public void itemStateChanged(ItemEvent arg0) {

		// Verifica item banco de dados selecionado
		Object fonte = arg0.getItemSelectable();
		int estado = arg0.getStateChange();

		if (estado == arg0.SELECTED) {

			if (fonte == banco1) {
				bancodedados = "oracle";
			}

			if (fonte == banco2) {
				bancodedados = "db2";
			}

			if (fonte == banco3) {
				bancodedados = "mssql";
			}

		}

	}

	public void actionPerformed(ActionEvent arg0) {
		// Efetua login no banco de dados
		Cursor acesso = new Cursor();

		if (bancodedados == "oracle") {
			acesso.conectar("daberto", "p@55w0rd");
		}

		if (bancodedados == "db2") {
			acesso.conectar("db2admin", "p@55w0rd");
		}

		if (bancodedados == "mssql") {
			acesso.conectar("devaberto", "p@55w0rd");
		}

		try {
			// Executa Stored Procedure
			acesso.executaProcedure();
			// Executa Intrução SQL
			tabela.setModel(criaTableModel(acesso.retornaTabela()));
		} catch (SQLException e) {
			e.printStackTrace();
		}

	}

	public Container criaPainel() throws SQLException {
		// Cria painel principal
		JPanel painel = new JPanel();

		// Seleciona layout
		painel.setLayout(new BoxLayout(painel, BoxLayout.PAGE_AXIS));
		painel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));

		// Cria painel de escolha de conexão
		JPanel pescolha = new JPanel();

		pescolha.setLayout(new BoxLayout(pescolha, BoxLayout.LINE_AXIS));
		pescolha.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

		ButtonGroup grupo = new ButtonGroup();

		// Cria componentes de radio
		banco1 = new JRadioButton("Oracle");
		banco2 = new JRadioButton("IBM DB2");
		banco3 = new JRadioButton("Microsoft SQL");

		// Agrupa botões de radio
		grupo.add(banco1);
		grupo.add(banco2);
		grupo.add(banco3);

		// Cria ouvinte dos botões
		banco1.addItemListener(this);
		banco2.addItemListener(this);
		banco3.addItemListener(this);

		// Seleciona primeira conexão
		banco1.setSelected(true);

		// Adiciona botões ao painel
		pescolha.add(banco1);
		pescolha.add(banco2);
		pescolha.add(banco3);

		// Efetua primeira conexão ao banco (ORACLE)
		Cursor acesso = new Cursor();

		acesso.conectar("daberto", "p@55w0rd");

		// Cria modelo de tabela
		tabela = new JTable(criaTableModel(acesso.retornaTabela()));

		// Adiciona um painel de rolagem
		JScrollPane rolar = new JScrollPane(tabela);

		// Cria painel do botão
		JPanel pbotao = new JPanel();

		// Seleciona layout
		pbotao.setLayout(new BoxLayout(pbotao, BoxLayout.PAGE_AXIS));

		// cria botão
		botao = new JButton("Executar Stored Procedure");
		botao.addActionListener(this);

		// Adiciona botão ao painel
		pbotao.add(botao);

		// componentes ao painel principal
		painel.add(pescolha);
		painel.add(rolar);
		painel.add(Box.createVerticalStrut(10));
		painel.add(botao);

		return painel;
	}

	public static void criaGUI() {
		// Cria formulario
		JFrame formulario = new JFrame("Desenvolvimento Aberto - Cursor");
		formulario.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		// cria painel de conteudo
		Cursor acesso = new Cursor();

		try {
			formulario.setContentPane(acesso.criaPainel());
		}

		catch (SQLException e) {
			e.printStackTrace();
		}

		// Exibe o formulario
		formulario.setSize(700, 300);
		formulario.setVisible(true);
	}

	public static void main(String[] args) {
		javax.swing.SwingUtilities.invokeLater(new Runnable() {

			@Override
			public void run() {

				// Cria e mostra a GUI
				criaGUI();

			}
		});
	}
}

SQL – A instrução a seguir, substitui o cursor em um único acesso. (Oracle)

-- Este script SQL efetua o mesmo procedimento do cursor
-- utilizando apenas um acesso ao banco de dados.
-- é importante projetar seu acesso a dados para obter alta performance
-- você deve utilizar cursores apenas quando realmente for necessário.
--
-- Para os outros bancos substitua o campo SYSDATE
-- Para DB2 use Current date
-- Para MSSQL use GETDATE() 

Insert into SALARIO
Select A.ID_FUNCIONARIO,SYSDATE, ((A.SALARIO * B.PORCENTAGEM)/100)
  from FUNCIONARIOS A, DESCONTO B
  Where
  A.ID_FUNCIONARIO = B.ID_FUNCIONARIO;